<!-- filepath: c:\Users\1\Desktop\SeekReal\src\views\Message.vue 2025/7/23 徐安修改 处理页面已读异常-->
<template>
  <div class="message-center">
    <!-- 横向四个选项卡 -->
    <div class="horizontal-tabs">
      <div 
        v-for="tab in mainTabs" 
        :key="tab.value"
        class="main-tab"
        :class="{ active: category === tab.value }"
        @click="selectMainTab(tab.value)"
      >
        <text class="tab-name">{{ tab.label }}</text>
        <text v-if="tab.count > 0" class="tab-count">{{ tab.count }}</text>
        <view v-if="tab.unreadCount > 0" class="unread-dot"></view>
      </div>
    </div>
    
    <!-- 子类型选项卡（仅在需要时显示） -->
    <div v-if="showSubTabs" class="sub-tabs">
      <div 
        v-for="subTab in currentSubTabs" 
        :key="subTab.value"
        class="sub-tab"
        :class="{ active: type === subTab.value }"
        @click="selectSubTab(subTab.value)"
      >
        {{ subTab.label }}
      </div>
    </div>

    <!-- 筛选器 -->
    <div class="filters">
      
      <div class="filter-group">
        <label>
          <!-- 状态筛选 -->
          <!-- #ifdef H5 -->
          <select v-model="status" class="filter-select kawaii-select" @change="applyFilter">
            <option v-for="option in currentStatusOptions" :key="option.value" :value="option.value">
              {{ option.label }}
            </option>
          </select>
          <!-- #endif -->
          <!-- #ifdef MP-WEIXIN -->
          <picker :range="currentStatusOptions" range-key="label" :value="statusIndex" @change="onStatusPickerChange" class="filter-select kawaii-select">
            <view class="uni-input">{{ currentStatusOptions[statusIndex].label }}</view>
          </picker>
          <!-- #endif -->
        </label>
      </div>
      
      <button @click="resetFilters" class="reset-btn kawaii-btn">
        <span class="btn-icon">↻</span> 重置筛选
      </button>
      
      <!-- 一键已读按钮（仅系统消息标签页显示） -->
      <button v-if="category === 'system'" @click="markAllSystemMessagesRead" class="mark-all-read-btn kawaii-btn">
        <span class="btn-icon">✓</span> 一键已读
      </button>
    </div>
    
    <!-- 只显示一个状态 -->
    
    <div v-show="!loading && !error" class="message-list">
      <div
        v-for="msg in messages.content"
        :key="msg.id"
        class="message-card"
        :class="{ 
          unread: msg.status === 0,
          approved: msg.status === 1,  // 已投同意
          rejected: msg.status === 2,   // 已投拒绝
          withdrawn: msg.status === 4,  // 已撤回
          invalid: msg.status === 5,    // 已结束（投票提前结束或时间到期）
          processed: msg.status !== 0  // 除了待投票以外的所有消息
        }"
        @click="handleMessageClickOrJump(msg)"
      >
        <div class="msg-title">{{ msg.title }}</div>
        <div class="msg-meta">
  <span class="type-badge" :class="`type-${resolveMessageType(msg)}`">{{ typeText(resolveMessageType(msg), msg) }}</span>
  <span class="status-badge" :class="`status-${msg.status}`">{{ statusText(msg.status, resolveMessageType(msg), msg) }}</span>
          <span class="msg-time">{{ msg.createTime ? formatTime(msg.createTime) : '-' }}</span>
  <!-- 投票消息倒计时（仅未投票且未过期时显示） -->
  <span v-if="msg.status === 0 && msgCountdownMap[msg.id] > 0 && !isExpired(msg) && (resolveMessageType(msg) === MessageType.TEAM_VOTE || resolveMessageType(msg) === MessageType.TASK_MODIFICATION_VOTE)"
        :class="['countdown-badge', 'vote-countdown', { urgent: msgCountdownMap[msg.id] <= 30 }]">
    ⏱️ {{ formatCountdown(msgCountdownMap[msg.id]) }}
  </span>
        </div>
        <!-- 工作核验消息显示过期时间和核验状态 -->
        <div v-if="msg.type === MessageType.WORK_VERIFICATION" class="work-verify-info">
          <div v-if="getExpiresAt(msg)" class="expires-info">
            <span class="expires-label">过期时间：</span>
            <span class="expires-time" :class="{ 'expired': isExpired(msg) }">{{ formatExpiresTime(getExpiresAt(msg)) }}</span>
          </div>
          <div class="verification-status">
            <span class="status-label">核验状态：</span>
            <span class="status-value" :class="getVerificationStatusClass(msg)">
              {{ getVerificationStatusText(msg) }}
            </span>
          </div>
        </div>
        
        <!-- AI远程提问消息显示预约信息 -->
        <div v-if="msg.type === MessageType.AI_EXAM" class="ai-exam-info">
          <div v-if="getExamScheduledTime(msg)" class="scheduled-info">
            <span class="scheduled-label">预约时间：</span>
            <span class="scheduled-time">{{ formatScheduledTime(getExamScheduledTime(msg)) }}</span>
          </div>
          <div class="exam-status">
            <span class="status-label">考试状态：</span>
            <span class="status-value" :class="getExamStatusClass(msg)">
              {{ getExamStatusText(msg) }}
            </span>
          </div>
        </div>
        
      </div>
    </div>
      <div class="pagination">
        <button :disabled="page === 0" @click="changePage(page-1)" class="kawaii-btn page-btn">
          <span class="btn-icon">◀</span> 上一页
        </button>
        <div class="page-info">
          <span class="page-text">第 {{ page+1 }} / {{ messages.totalPages || 1 }} 页</span>
          <span class="count-text">共 {{ messages.totalElements || 0 }} 条</span>
        </div>
        <button :disabled="page+1 >= messages.totalPages" @click="changePage(page+1)" class="kawaii-btn page-btn">
          下一页 <span class="btn-icon">▶</span>
        </button>
      </div>
    <div v-if="actionMsg" class="action-msg">
      <span class="action-icon">✨</span>
      {{ actionMsg }}
    </div>
    <!-- 工作核验弹窗 -->
    <div v-if="showWorkVerifyDialog" class="work-verify-overlay">
      <div class="work-verify-dialog kawaii-card">
        <div class="dialog-decoration top-left"></div>
        <div class="dialog-decoration top-right"></div>
        <div class="dialog-decoration bottom-left"></div>
        <div class="dialog-decoration bottom-right"></div>
        <h3 class="dialog-title">
          <span class="dialog-icon">📷</span>
          工作核验
        </h3>
        <div class="verify-hint">请在15分钟有效期内完成一次人脸核验</div>
        
        <!-- 摄像头区域 -->
        <div class="camera-container">
          <!-- #ifdef H5 -->
          <div class="video-container" ref="videoWrap"></div>
          <!-- #endif -->
          <!-- #ifdef APP-PLUS || MP-WEIXIN -->
          <camera 
            v-if="isCameraActive" 
            device-position="front" 
            flash="off" 
            class="camera-view"
            @error="onCameraError"
          ></camera>
          <!-- #endif -->
          <div v-if="!isCameraActive" class="camera-placeholder">
            <div class="placeholder-icon">📷</div>
            <div class="placeholder-text">摄像头未开启</div>
          </div>
        </div>
        
        <!-- 控制按钮 -->
        <div class="verify-controls">
          <button @click="toggleCamera" class="kawaii-btn camera-btn">
            <span class="btn-icon">{{ isCameraActive ? '📷' : '📷' }}</span>
            {{ isCameraActive ? '关闭摄像头' : '开启摄像头' }}
          </button>
          <button 
            @click="startVerification" 
            :disabled="!isCameraActive || isVerifying"
            class="kawaii-btn verify-btn"
          >
            <span class="btn-icon">✓</span>
            {{ isVerifying ? '核验中...' : '开始核验' }}
          </button>
        </div>
        
        <!-- 状态显示 -->
        <div v-if="verifyStatus" class="verify-status" :class="verifyStatus.type">
          <span class="status-icon">{{ verifyStatus.icon }}</span>
          {{ verifyStatus.message }}
        </div>
        
        <!-- 关闭按钮 -->
        <div class="dialog-buttons">
          <button @click="closeWorkVerifyDialog" class="kawaii-btn cancel-btn">
            <span class="btn-icon">↩</span> 关闭
          </button>
        </div>
      </div>
    </div>

    <!-- AI远程提问预约弹窗 -->
    <div v-if="showExamScheduleDialog" class="exam-schedule-overlay">
      <div class="exam-schedule-dialog kawaii-card">
        <div class="dialog-decoration top-left"></div>
        <div class="dialog-decoration top-right"></div>
        <div class="dialog-decoration bottom-left"></div>
        <div class="dialog-decoration bottom-right"></div>
        <h3 class="dialog-title">
          <span class="dialog-icon">📅</span>
          预约考试时间
        </h3>
        <div class="exam-info">
          <div class="info-item">
            <span class="info-label">考试类型：</span>
            <span class="info-value">AI远程提问</span>
          </div>
          <div class="info-item">
            <span class="info-label">问题数量：</span>
            <span class="info-value">{{ currentExamInfo.questionCount }} 题</span>
          </div>
          <div class="info-item">
            <span class="info-label">预计时长：</span>
            <span class="info-value">{{ currentExamInfo.questionCount * 10 }} 分钟</span>
          </div>
        </div>
        <div class="schedule-form">
          <label class="form-label">选择考试时间：</label>
          <!-- #ifdef H5 -->
          <input 
            v-model="scheduledDateTime" 
            type="datetime-local" 
            class="kawaii-input datetime-input"
            :min="getMinDateTime()"
          />
          <!-- #endif -->
          <!-- #ifdef MP-WEIXIN -->
          <picker 
            mode="multiSelector" 
            :value="dateTimePickerValue" 
            :range="dateTimePickerRange"
            @change="onDateTimePickerChange"
            class="datetime-picker"
          >
            <view class="datetime-picker-display">
              {{ formatDateTimePickerDisplay() }}
            </view>
          </picker>
          <!-- #endif -->
        </div>
        <div class="dialog-buttons">
          <button @click="confirmExamSchedule" class="kawaii-btn confirm-btn">
            <span class="btn-icon">✓</span> 确认预约
          </button>
          <button @click="cancelExamSchedule" class="kawaii-btn cancel-btn">
            <span class="btn-icon">↩</span> 取消
          </button>
        </div>
      </div>
    </div>

    <!-- 团队邀请弹窗 -->
    <div v-if="showTeamInvitationModal" class="team-invitation-overlay">
      <div class="team-invitation-dialog kawaii-card">
        <div class="dialog-decoration top-left"></div>
        <div class="dialog-decoration top-right"></div>
        <div class="dialog-decoration bottom-left"></div>
        <div class="dialog-decoration bottom-right"></div>
        <h3 class="dialog-title">
          <span class="dialog-icon">👥</span>
          团队邀请
        </h3>
        
        <!-- 邀请人信息 -->
        <div v-if="currentTeamInvitation" class="invitation-content">
          <div class="inviter-section">
            <div class="inviter-avatar">
              <image 
                :src="currentTeamInvitation.leaderAvatar || '/static/images/default-avatar.png'" 
                class="avatar-img"
                @error="handleAvatarError"
              />
            </div>
            <div class="inviter-details">
              <div class="inviter-name">{{ currentTeamInvitation.leaderNickname || '未知用户' }}</div>
              <div class="inviter-role">邀请您加入团队</div>
            </div>
          </div>
          
          <!-- 团队信息 -->
          <div class="team-section">
            <div class="team-info">
              <span class="team-label">团队名称：</span>
              <span class="team-name">{{ currentTeamInvitation.teamName || '未命名团队' }}</span>
            </div>
          </div>
          
          <!-- 任务信息（可点击跳转） -->
          <div v-if="currentTeamInvitation.taskTitle" class="task-section" @click="jumpToTaskFromModal(currentTeamInvitation.taskId)">
            <div class="task-header">
              <span class="task-label">相关任务：</span>
              <span class="task-jump-hint">点击查看详情 →</span>
            </div>
            <div class="task-title">{{ currentTeamInvitation.taskTitle }}</div>
            <div v-if="currentTeamInvitation.taskContent" class="task-content">
              {{ currentTeamInvitation.taskContent }}
            </div>
            
            <!-- 任务详细信息 -->
            <div class="task-details">
              <div v-if="currentTeamInvitation.taskReward" class="task-reward">
                <span class="detail-label">💰 奖励：</span>
                <span class="detail-value">{{ currentTeamInvitation.taskReward }} 元</span>
              </div>
              <div v-if="currentTeamInvitation.taskDeadline" class="task-deadline">
                <span class="detail-label">⏰ 截止：</span>
                <span class="detail-value">{{ formatTaskDeadline(currentTeamInvitation.taskDeadline) }}</span>
              </div>
              <div v-if="currentTeamInvitation.taskLocation" class="task-location">
                <span class="detail-label">📍 地点：</span>
                <span class="detail-value">{{ currentTeamInvitation.taskLocation }}</span>
              </div>
              <div v-if="currentTeamInvitation.taskStatus" class="task-status">
                <span class="detail-label">📋 状态：</span>
                <span class="detail-value" :class="getTaskStatusClass(currentTeamInvitation.taskStatus)">
                  {{ getTaskStatusText(currentTeamInvitation.taskStatus) }}
                </span>
              </div>
            </div>
          </div>
        </div>
        
        <!-- 操作按钮 -->
        <div class="dialog-buttons">
          <button @click="handleTeamInvitationAccept" class="kawaii-btn confirm-btn">
            <span class="btn-icon">✓</span> 同意加入
          </button>
          <button @click="handleTeamInvitationReject" class="kawaii-btn cancel-btn">
            <span class="btn-icon">✗</span> 拒绝邀请
          </button>
          <button @click="handleTeamInvitationPostpone" class="kawaii-btn postpone-btn">
            <span class="btn-icon">⏰</span> 暂不处理
          </button>
        </div>
      </div>
    </div>

    <!-- 弹窗等其它内容保留 -->
    <div v-if="showReasonDialog" class="reason-dialog-overlay">
      <div class="reason-dialog kawaii-card">
        <div class="dialog-decoration top-left"></div>
        <div class="dialog-decoration top-right"></div>
        <div class="dialog-decoration bottom-left"></div>
        <div class="dialog-decoration bottom-right"></div>
        <h3 class="dialog-title">
          <span class="dialog-icon">📝</span>
          {{ dialogTitle }}
        </h3>
        <textarea 
          v-model="reasonText" 
          rows="4" 
          :placeholder="dialogPlaceholder"
          class="reason-textarea kawaii-input"
        ></textarea>
        <!-- 任务完成申请的详情预览（只读） -->
        <div v-if="currentAction.type === 'task-completion'" class="completion-preview">
          <div v-if="previewLoading" class="kawaii-loading">
            <span class="loading-dot"></span>
            <span class="loading-dot"></span>
            <span class="loading-dot"></span>
            <div class="loading-text">加载申请详情...</div>
          </div>
          <div v-else-if="previewError" class="kawaii-error">
            <div class="error-icon">⚠️</div>
            <div class="error-text">{{ previewError }}</div>
          </div>
          <div v-else-if="completePreview" class="preview-content">
            <div class="preview-block">
              <div class="preview-label">完成证明</div>
              <div class="preview-proof">{{ completePreview.proof || '—' }}</div>
            </div>
            <div class="preview-block">
              <div class="preview-label">铭牌分配</div>
              <div v-if="completePreview.badges && completePreview.badges.length">
                <div v-for="b in completePreview.badges" :key="b.badgeId || b.id" class="preview-badge">
                  <div class="badge-title">{{ badgeTitle(b) }}</div>
                  <div class="preview-contributor" v-for="(c, idx) in b.contributors" :key="idx">
                    <div class="contributor-header">
                      <image 
                        :src="getUserAvatar(c.uid)" 
                        class="contributor-avatar" 
                        @error="handleAvatarError"
                      />
                      <div class="contributor-info">
                        <div class="contributor-name">{{ getUserName(c.uid) || `用户${c.uid}` }}</div>
                        <div class="contributor-uid">UID: {{ c.uid }}</div>
                      </div>
                    </div>
                    <div class="field-row">
                      <div class="field-label">角色</div>
                      <div class="field-value">{{ c.role || '—' }}</div>
                    </div>
                    <div class="field-row">
                      <div class="field-label">贡献比例(%)</div>
                      <div class="field-value">{{ c.percentage }}%</div>
                    </div>
                  </div>
                </div>
              </div>
              <div v-else class="field-empty">暂无分配数据</div>
            </div>
          </div>
        </div>
        <!-- 任务申请团队信息显示 -->
        <div v-if="currentAction.type === 'task-apply' && currentAction.teamInfo" class="team-info-preview">
          <div class="preview-block">
            <div class="preview-label">团队信息</div>
            <div class="team-details">
              <div class="team-name">团队名称：{{ currentAction.teamInfo.teamName || '—' }}</div>
              <div class="team-leader">
                <image 
                  :src="getUserAvatar(currentAction.teamInfo.leaderId)" 
                  class="leader-avatar" 
                  @error="handleAvatarError"
                />
                <div class="leader-info">
                  <div class="leader-name">{{ getUserNickname(currentAction.teamInfo.leaderId) }}</div>
                  <div class="leader-uid">UID {{ currentAction.teamInfo.leaderId }}</div>
                </div>
                <div class="leader-role-badge">队长</div>
              </div>
              <div class="team-members">
                <div class="members-label">团队成员：</div>
                <div v-for="member in currentAction.teamInfo.members" :key="member.userId" class="member-item">
                  <image 
                    :src="getUserAvatar(member.userId)" 
                    class="member-avatar" 
                    @error="handleAvatarError"
                  />
                  <div class="member-info">
                    <div class="member-name">{{ getUserNickname(member.userId) }}</div>
                    <div class="member-uid">UID {{ member.userId }}</div>
                  </div>
                  <div class="member-role" :class="member.role === 'leader' ? 'leader-role' : 'member-role'">
                    {{ member.role === 'leader' ? '队长' : '队员' }}
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="dialog-buttons">
          <button @click="handleApprove" class="kawaii-btn approve-btn">
            <span class="btn-icon">✓</span> 同意
          </button>
          <button @click="handleReject" class="kawaii-btn reject-btn">
            <span class="btn-icon">✗</span> 拒绝
          </button>
          <button @click="cancelAction" class="kawaii-btn cancel-btn">
            <span class="btn-icon">↩</span> 取消
          </button>
        </div>
      </div>
    </div>
    <!-- 任务修改申请弹窗 -->
    <div v-if="showTaskModificationModal" class="modal-overlay" @click.self="closeTaskModificationModal">
      <div class="modal-content task-modification-modal" @click.stop>
        <div class="modal-header">
          <text class="modal-title">任务修改申请详情</text>
          <button class="modal-close" @click="closeTaskModificationModal"><text>×</text></button>
        </div>
        <div class="modal-body">
          <div class="modification-info">
            <text class="info-label">修改内容：</text>
            <div class="modification-details">
              <div class="detail-item">
                <text class="detail-label">任务标题：</text>
                <text class="detail-value">{{ taskModificationData?.title || '无' }}</text>
              </div>
              <div class="detail-item">
                <text class="detail-label">任务描述：</text>
                <text class="detail-value">{{ taskModificationData?.description || '无' }}</text>
              </div>
              <div class="detail-item">
                <text class="detail-label">截止时间：</text>
                <text class="detail-value">{{ taskModificationData?.deadline || '无' }}</text>
              </div>
              <div class="detail-item">
                <text class="detail-label">奖励金额：</text>
                <text class="detail-value">{{ taskModificationData?.reward || 0 }}元</text>
              </div>
              <div class="detail-item">
                <text class="detail-label">联系方式：</text>
                <text class="detail-value">{{ taskModificationData?.contactName || '无' }} - {{ taskModificationData?.contactPhone || '无' }}</text>
              </div>
            </div>
          </div>
          
          <!-- 铭牌预览 -->
          <div v-if="taskModificationBadges && taskModificationBadges.length > 0" class="badge-preview">
            <text class="info-label">修改后的铭牌分配：</text>
            <div class="badge-list">
              <div v-for="(badge, index) in taskModificationBadges" :key="index" class="badge-item">
                <text class="badge-name">{{ badge.name }}</text>
                <text class="badge-level">等级 {{ badge.level }}</text>
                <text class="badge-description">{{ badge.description }}</text>
              </div>
            </div>
          </div>
        </div>
        <div class="modal-footer">
          <button class="modal-cancel-btn" @click="rejectTaskModification">
            <text>拒绝</text>
          </button>
          <button class="modal-confirm-btn" @click="approveTaskModification">
            <text>同意</text>
          </button>
        </div>
      </div>
    </div>

    <!-- 投票模态框 -->
    <div v-if="showVoteModalFlag" class="vote-modal-overlay">
      <div class="vote-modal kawaii-card">
        <!-- 调试信息 -->
        <div style="background: #f0f0f0; padding: 10px; margin-bottom: 10px; border-radius: 4px;">
          <div>调试信息:</div>
          <div>showVoteModalFlag: {{ showVoteModalFlag }}</div>
          <div>currentVoteMessage: {{ currentVoteMessage?.title }}</div>
          <div>voteData: {{ voteData ? '已设置' : '未设置' }}</div>
          <div v-if="voteData?.vote">投票类型: {{ voteData.vote.voteType }} (1=踢人, 2=弹劾, 3=选举)</div>
          <div v-if="voteData?.vote">投票标题: {{ voteData.vote.voteTitle }}</div>
          <div v-if="voteData?.vote">已投票: {{ voteData.vote.hasVoted ? '是' : '否' }}</div>
        </div>
        <div class="dialog-decoration top-left"></div>
        <div class="dialog-decoration top-right"></div>
        <div class="dialog-decoration bottom-left"></div>
        <div class="dialog-decoration bottom-right"></div>
        
        <div class="vote-header">
          <h3 class="vote-title">
            <span class="vote-icon">🗳️</span>
            {{ currentVoteMessage?.title || '投票' }}
          </h3>
          <button @click="closeVoteModal" class="close-btn">×</button>
        </div>
        
        <!-- 任务详情 -->
        <div v-if="voteData?.task" class="task-detail-section">
          <div class="task-info">
            <div class="task-title">{{ voteData.task.title }}</div>
            <div class="task-reward">悬赏：{{ formatNumber(voteData.task.reward) }} 元</div>
            <div class="task-status">状态：{{ getStatusText(voteData.task.status) }}</div>
          </div>
          <button @click="goToTaskDetail" class="task-detail-btn">
            <span class="btn-icon">📋</span> 查看任务详情
          </button>
        </div>
        
        <!-- 投票进度 -->
        <div class="vote-progress-section">
          <div class="progress-info">
            <span class="progress-label">投票结果</span>
          </div>
          <div class="vote-results">
            <div class="result-item">
              <span class="result-label">同意：</span>
              <span class="result-count approve">{{ voteData?.vote?.approveCount || 0 }}人</span>
            </div>
            <div class="result-item">
              <span class="result-label">反对：</span>
              <span class="result-count reject">{{ voteData?.vote?.rejectCount || 0 }}人</span>
            </div>
            <div class="result-item">
              <span class="result-label">未投：</span>
              <span class="result-count pending">{{ voteData?.vote?.pendingCount || 0 }}人</span>
            </div>
          </div>
          <div class="vote-rules">
            <span class="rule-text">
              {{ getVoteRuleText(voteData?.vote) }}
            </span>
          </div>
        </div>
        
        <!-- 投票原因 -->
        <div class="vote-reason-section">
          <div class="reason-label">投票原因：</div>
          <div class="reason-content">{{ currentVoteMessage?.content || '无' }}</div>
        </div>
        
        <!-- 选举新队长候选人选择 -->
        <div v-if="voteData?.vote?.voteType === 3 && !voteData?.vote?.hasVoted" class="candidate-selection">
          <div style="margin-bottom: 15px; font-weight: bold; color: #2c3e50;">选择新队长候选人：</div>
          <div class="candidates-list">
            <div 
              v-for="candidate in getCandidates()" 
              :key="candidate.userId"
              @click="selectCandidate(candidate.userId)"
              class="candidate-item"
              :class="{ 'selected': selectedCandidate === candidate.userId }"
            >
              <image 
                :src="getUserAvatar(candidate.userId)" 
                class="candidate-avatar" 
                @error="handleAvatarError"
              />
              <div class="candidate-info">
                <div class="candidate-name">{{ getUserNickname(candidate.userId) }}</div>
                <div class="candidate-uid">UID {{ candidate.userId }}</div>
              </div>
              <div v-if="selectedCandidate === candidate.userId" class="selected-icon">✓</div>
            </div>
          </div>
          <div class="candidate-actions">
            <button 
              @click="submitCandidateVote" 
              class="kawaii-btn approve-btn"
              :disabled="!selectedCandidate || voteLoading"
              style="background: #28a745; color: white; padding: 10px 20px; border: none; border-radius: 5px; margin: 5px;"
            >
              <span class="btn-icon">✓</span> 选择此人为新队长
            </button>
            <button 
              @click="submitVote('abstain')" 
              class="kawaii-btn abstain-btn"
              :disabled="voteLoading"
              style="background: #6c757d; color: white; padding: 10px 20px; border: none; border-radius: 5px; margin: 5px;"
            >
              <span class="btn-icon">—</span> 弃权
            </button>
          </div>
        </div>

        <!-- 普通投票按钮（踢人、弹劾等） -->
        <div class="vote-buttons" style="background: #f0f0f0; padding: 10px; border: 2px solid red;" v-else-if="!voteData?.vote?.hasVoted">
          <div style="margin-bottom: 10px; font-weight: bold;">投票按钮区域:</div>
          <div style="margin-bottom: 10px; color: #666; font-size: 12px;">
            投票类型: {{ getVoteTypeText(voteData?.vote?.voteType) }}
          </div>
          <button 
            @click="submitVote('approve')" 
            class="kawaii-btn approve-btn"
            :disabled="voteLoading"
            style="background: #28a745; color: white; padding: 10px 20px; border: none; border-radius: 5px; margin: 5px;"
          >
            <span class="btn-icon">✓</span> 同意
          </button>
          <button 
            @click="submitVote('reject')" 
            class="kawaii-btn reject-btn"
            :disabled="voteLoading"
            style="background: #dc3545; color: white; padding: 10px 20px; border: none; border-radius: 5px; margin: 5px;"
          >
            <span class="btn-icon">✗</span> 反对
          </button>
          <button 
            @click="submitVote('abstain')" 
            class="kawaii-btn abstain-btn"
            :disabled="voteLoading"
            style="background: #6c757d; color: white; padding: 10px 20px; border: none; border-radius: 5px; margin: 5px;"
          >
            <span class="btn-icon">—</span> 不选
          </button>
        </div>
        
        <!-- 已投票状态显示 -->
        <div class="vote-status" style="background: #e8f5e8; padding: 15px; border: 2px solid #28a745; border-radius: 8px; text-align: center;" v-else>
          <div style="font-weight: bold; color: #28a745; margin-bottom: 10px;">✓ 您已投票</div>
          <div style="color: #333; margin-bottom: 15px;">
            <!-- 选举投票显示已选择的候选人 -->
            <div v-if="voteData?.vote?.voteType === 3">
              <div v-if="voteData?.vote?.userVote === true && voteData?.vote?.selectedCandidate">
                已选择: {{ getUserNickname(voteData.vote.selectedCandidate) }}
              </div>
              <div v-else-if="voteData?.vote?.userVote === false">
                反对选举
              </div>
              <div v-else>
                弃权
              </div>
            </div>
            <!-- 普通投票显示 -->
            <div v-else>
              {{ voteData?.vote?.userVote === true ? '同意' : (voteData?.vote?.userVote === false ? '反对' : '弃权') }}
            </div>
          </div>
          <button 
            @click="withdrawVote" 
            class="withdraw-btn"
            :disabled="withdrawLoading"
            style="background: #ffc107; color: #333; padding: 8px 16px; border: none; border-radius: 5px; font-size: 14px;"
          >
            {{ withdrawLoading ? '撤回中...' : '撤回投票' }}
          </button>
        </div>
        
        <!-- 加载状态 -->
        <div v-if="voteLoading" class="vote-loading">
          <span class="loading-icon">⏳</span>
          处理中...
        </div>
      </div>
    </div>

    <!-- 底部装饰波浪 -->
    <div class="bottom-decoration">
      <svg viewBox="0 0 500 60" preserveAspectRatio="none">
        <path d="M0,30 Q125,60 250,30 T500,30 V60 H0 Z" fill="#e3eafc"/>
        <path d="M0,40 Q125,70 250,40 T500,40 V60 H0 Z" fill="#f5f7ff"/>
      </svg>
    </div>
  </div>
</template>

<script setup>
import { NETWORK_CONFIG } from '@/config/network.js'
import { ref, onMounted, nextTick, watch } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import { MessageType, UserApplicationStatus, VoteMessageStatus, VerificationMessageStatus, SystemNotificationStatus, MessageUtils, getStatusDescriptionByType } from '@/utils/messageTypes';
import { useUserStore } from '@/store/user';
import { reactive, onBeforeUnmount } from 'vue';
const msgCountdownMap = reactive({});
let msgCountdownTimer = null;


// 主选项卡配置
const mainTabs = ref([
  { value: 'system', label: '系统消息', count: 0, unreadCount: 0 },
  { value: 'user', label: '用户事务', count: 0, unreadCount: 0 },
  { value: 'vote', label: '投票事务', count: 0, unreadCount: 0 },
  { value: 'verification', label: '远程核验', count: 0, unreadCount: 0 }
]);

// 子类型选项卡配置
const subTabsConfig = {
  system: [
    { value: '', label: '全部系统通知' },
    { value: '-1', label: '通用通知' },
    { value: '-10', label: '任务申请通过' },
    { value: '-11', label: '任务申请拒绝' },
    { value: '-12', label: '任务完成通知' },
    { value: '-13', label: '任务取消通知' },
    { value: '-14', label: '组织申请通过' },
    { value: '-15', label: '组织申请拒绝' },
    { value: '-16', label: '申请已提交' },
    { value: '-17', label: '申请已撤回' },
    { value: '-18', label: '取消申请已发送' },
    { value: '-19', label: '任务申请撤回' },
    { value: '-20', label: '任务完成申请撤回' },
    { value: '-21', label: '任务取消申请处理结果' },
    { value: '-24', label: '团队邀请已同意' },
    { value: '-25', label: '团队邀请已拒绝' },
    { value: '-26', label: '团队邀请已撤回' },
    { value: '-27', label: '团队任务已申请' },
    { value: '-28', label: '团队任务已开始' },
    { value: '-29', label: '团队任务已取消' },
    { value: '-30', label: '团队任务无需处理' }
  ],
  user: [
    { value: '', label: '全部用户申请' },
    { value: '2', label: '好友请求' },
    { value: '3', label: '任务申请' },
    { value: '4', label: '任务完成' },
    { value: '5', label: '取消申请' },
    { value: '10', label: '团队邀请' }
  ],
  vote: [
    { value: '', label: '全部投票消息' },
    { value: '16', label: '团队投票' },
    { value: '17', label: '任务投票' },
    { value: '18', label: '选举投票' }
  ],
  verification: [
    { value: '', label: '全部核验消息' },
    { value: '64', label: '核验消息' },
    { value: '65', label: '工作核验' },
    { value: '66', label: 'AI远程提问' }
  ]
};

// 分类筛选选项
const categoryOptions = [
  { value: 'system', label: '系统通知' },
  { value: 'user', label: '用户申请' },
  { value: 'verification', label: '核验相关' },
  { value: 'vote', label: '投票消息' }
];

// 各分类的状态选项配置
const statusOptionsConfig = {
  system: [
    { value: '', label: '全部状态' },
    { value: '0', label: '未读' },
    { value: '1', label: '已读' }
  ],
  user: [
    { value: '', label: '全部状态' },
    { value: '0', label: '待处理' },
    { value: '1', label: '已同意' },
    { value: '2', label: '已拒绝' },
    { value: '3', label: '已撤回' }
  ],
  vote: [
    { value: '', label: '全部状态' },
    { value: '0', label: '待投票' },
    { value: '1', label: '已投同意' },
    { value: '2', label: '已投拒绝' },
    { value: '4', label: '已撤回' },
    { value: '5', label: '已结束' }
  ],
  verification: [
    { value: '', label: '全部状态' },
    { value: '0', label: '未处理' },
    { value: '1', label: '已处理' },
    { value: '2', label: '已过期' }
  ]
};

// 当前状态选项（根据选中的主选项卡动态变化）
const currentStatusOptions = ref([]);

// 当前子选项卡
const currentSubTabs = ref([]);
const showSubTabs = ref(false);

// 用户信息缓存
const userInfoCache = ref({});

// 选择主选项卡
const selectMainTab = (tabValue) => {
  category.value = tabValue;
  type.value = ''; // 重置子类型选择
  currentSubTabs.value = subTabsConfig[tabValue] || [];
  showSubTabs.value = currentSubTabs.value.length > 1; // 如果只有一个"全部"选项，不显示子选项卡
  
  // 更新状态选项
  currentStatusOptions.value = statusOptionsConfig[tabValue] || [];
  status.value = ''; // 重置状态筛选
  statusIndex.value = 0; // 重置状态索引
  
  page.value = 0;
  fetchMessages();
};

// 选择子选项卡
const selectSubTab = (subTabValue) => {
  type.value = subTabValue;
  page.value = 0;
  fetchMessages();
};

// 获取各分类的未读消息计数
const fetchUnreadCounts = async () => {
  try {
    const token = uni.getStorageSync('accessToken');
    
    const res = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/unread-count`,
      method: 'GET',
      header: { 'Access-Token': `Bearer ${token}` }
    });
    
    if (res.data.success) {
      const unreadCounts = res.data.unreadCounts;
      console.log('获取到未读消息计数:', unreadCounts);
      
      // 更新各选项卡的未读计数
      mainTabs.value.forEach(tab => {
        tab.unreadCount = unreadCounts[tab.value] || 0;
      });
      
      console.log('更新后的选项卡未读计数:', mainTabs.value);
      
      // 计算总未读数量（系统通知 + 用户申请 + 投票事务 + 核验消息）
      const totalUnread = (unreadCounts.system || 0) + 
                          (unreadCounts.user || 0) + 
                          (unreadCounts.vote || 0) + 
                          (unreadCounts.verification || 0);
      
      console.log('消息中心总未读数:', totalUnread);
      
      // 更新底部导航栏的红点（消息页面在第3个位置，索引为2）
      if (totalUnread > 0) {
        uni.setTabBarBadge({
          index: 2, // 消息页面在第3个位置
          text: totalUnread > 99 ? '99+' : totalUnread.toString()
        });
        console.log('设置消息中心tabBar红点:', totalUnread);
      } else {
        uni.removeTabBarBadge({
          index: 2
        });
        console.log('移除消息中心tabBar红点');
      }
    }
  } catch (e) {
    console.error('获取未读消息计数失败:', e);
  }
};

// 获取所有类型选项卡（扁平化）
const getAllTypeTabs = () => {
  const allTabs = [{ value: '', label: '全部' }];
  Object.values(subTabsConfig).forEach(group => {
    allTabs.push(...group);
  });
  return allTabs;
};

const typeTabs = getAllTypeTabs();

const type = ref("");
const status = ref("");
const category = ref("system"); // 默认显示系统消息
const page = ref(0);
const size = ref(10);
const loading = ref(false);
const error = ref('');
const messages = ref({ content: [], totalPages: 1, totalElements: 0 });
const actionMsg = ref('');

// 初始化子选项卡
currentSubTabs.value = subTabsConfig[category.value] || [];
showSubTabs.value = currentSubTabs.value.length > 1;

// 弹窗相关

// 投票相关状态
const showVoteModalFlag = ref(false);
const currentVoteMessage = ref(null);
const voteData = ref(null);
const voteLoading = ref(false);
const withdrawLoading = ref(false);

// 任务修改申请相关状态
const showTaskModificationModal = ref(false);
const taskModificationData = ref(null);
const taskModificationBadges = ref([]);
const currentTaskModificationMessage = ref(null);

// 选举新队长相关状态
const selectedCandidate = ref(null);
const showReasonDialog = ref(false);
const dialogTitle = ref('');
dialogTitle.value = '';
const dialogPlaceholder = ref('');
const reasonText = ref('');
const currentAction = ref({});
const applying = ref(false);

// 任务完成申请详情预览
const previewLoading = ref(false);
const previewError = ref('');
const completePreview = ref(null);

// 工作核验相关
const showWorkVerifyDialog = ref(false);
const isCameraActive = ref(false);
const isVerifying = ref(false);
const verifyStatus = ref(null);
const currentWorkVerificationId = ref('');
const videoWrap = ref(null);
const videoEl = ref(null);
const stream = ref(null);

// AI远程提问相关
const showExamScheduleDialog = ref(false);
const currentExamInfo = ref({ examTaskId: null, questionCount: 0, messageId: null });
const scheduledDateTime = ref('');

// 团队邀请弹窗相关
const showTeamInvitationModal = ref(false);
const currentTeamInvitation = ref(null);

// 时间选择器相关
const dateTimePickerValue = ref([0, 0, 0, 0]); // [年索引, 月索引, 日索引, 时索引]
const dateTimePickerRange = ref([[], [], [], []]); // [年数组, 月数组, 日数组, 时数组]

const badgeTitle = (b) => {
  const assoc = b.association || b.assoc;
  const level = b.level;
  const badgeId = b.badgeId || b.id;
  if (assoc && (level || level === 0)) return `徽章 Lv${level}`;
  if (badgeId) return `铭牌 #${badgeId}`;
  return '铭牌';
};

const fetchTaskCompletionPreview = async (taskId) => {
  previewLoading.value = true;
  previewError.value = '';
  completePreview.value = null;
  try {
    const token = uni.getStorageSync('accessToken');
    if (!token) throw new Error('请先登录');
    const tryUrls = [
      `${NETWORK_CONFIG.API_BASE_URL}/task/applycomplete/detail?taskId=${taskId}`
    ];
    let data = null;
    for (let i = 0; i < tryUrls.length; i++) {
      const res = await uni.request({
        url: tryUrls[i],
        method: 'GET',
        header: { 'Access-Token': `Bearer ${token}` },
        timeout: 8000
      });
      if (res.statusCode === 200 && res.data) {
        data = res.data;
        break;
      }
    }
    if (!data) throw new Error('未获取到申请完成详情');
    
    // 获取所有贡献者的UID
    const contributorUids = [];
    if (Array.isArray(data.badges)) {
      data.badges.forEach(badge => {
        if (Array.isArray(badge.contributors)) {
          badge.contributors.forEach(contributor => {
            if (contributor.uid) {
              contributorUids.push(contributor.uid);
            }
          });
        }
      });
    }
    
    // 批量获取用户信息
    if (contributorUids.length > 0) {
      await fetchUsersInfo(contributorUids);
    }
    
    completePreview.value = {
      proof: data.proof || data.reason || data.description || '',
      badges: Array.isArray(data.badges) ? data.badges : []
    };
  } catch (e) {
    previewError.value = e.message || '加载失败';
  } finally {
    previewLoading.value = false;
  }
};

// 显示投票模态框
const showVoteModal = async (message) => {
  try {
    console.log('showVoteModal 被调用，消息:', message);
    currentVoteMessage.value = message;
    voteLoading.value = true;
    
    // 获取任务详情和投票信息
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      console.log('没有找到token');
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }
    
    try {
      console.log('准备获取任务详情，taskId:', message.taskId);
      const taskResponse = await uni.request({
        url: `${NETWORK_CONFIG.API_BASE_URL}/task/detail?taskId=${message.taskId}`,
        method: 'GET',
        header: { 'Access-Token': `Bearer ${token}` }
      });
      
      console.log('任务详情响应:', taskResponse);
      
      if (taskResponse.data && taskResponse.statusCode === 200) {
        console.log('任务详情获取成功，准备获取投票信息');
        
        // 获取当前用户ID（从userStore）
        let userId = null;
        try {
          // 从userStore获取用户ID
          const userStore = useUserStore();
          if (userStore.profile && userStore.profile.uid) {
            userId = userStore.profile.uid;
            console.log('从userStore获取用户ID:', userId);
          } else {
            // 备用方案：从localStorage获取
            const currentUser = uni.getStorageSync('userInfo');
            if (currentUser && currentUser.uid) {
              userId = currentUser.uid;
              console.log('从localStorage获取用户ID:', userId);
            }
          }
        } catch (error) {
          console.error('获取用户ID失败:', error);
          userId = null;
        }
        
        if (!userId) {
          console.log('无法获取用户ID，跳过团队信息获取');
          // 即使没有用户ID也显示投票界面
          voteData.value = {
            task: taskData,
            message: message,
            vote: null,
            teamInfo: {}
          };
          showVoteModalFlag.value = true;
          await nextTick();
          return;
        }
        
        // 获取团队信息和投票数据
        const teamResponse = await uni.request({
          url: `${NETWORK_CONFIG.API_BASE_URL}/team/info?taskId=${message.taskId}&userId=${userId}`,
          method: 'GET',
          header: { 'Access-Token': `Bearer ${token}` }
        });
        
        console.log('团队信息响应:', teamResponse);
        console.log('团队信息数据结构:', JSON.stringify(teamResponse.data, null, 2));
        
        // 检查API响应是否成功
        if (teamResponse.statusCode !== 200 || !teamResponse.data || !teamResponse.data.success) {
          console.error('团队信息获取失败:', teamResponse);
          uni.showToast({ title: '获取团队信息失败', icon: 'none' });
          return;
        }
        
        // 构建投票数据
        const taskData = taskResponse.data;
        const teamData = teamResponse.data.teamInfo || {};
        const activeVotes = teamData.activeVotes || [];
        
        console.log('团队数据:', teamData);
        console.log('活跃投票列表:', activeVotes);
        console.log('活跃投票详情:', activeVotes.map(vote => ({
          voteId: vote.voteId,
          voteType: vote.voteType,
          voteTitle: vote.voteTitle,
          totalVoters: vote.totalVoters,
          votedCount: vote.votedCount,
          requiredVotes: vote.requiredVotes
        })));
        
        // 根据消息类型找到对应的投票
        let currentVote = null;
        
        console.log('消息标题:', message.title);
        console.log('查找投票逻辑:');
        
        // 如果消息标题包含"弹劾"，优先查找弹劾投票
        if (message.title?.includes('弹劾')) {
          console.log('消息包含"弹劾"，查找弹劾投票');
          currentVote = activeVotes.find(vote => 
            vote.voteType === 2 || vote.voteTitle?.includes('弹劾')
          );
          console.log('找到的弹劾投票:', currentVote);
        }
        // 如果消息标题包含"踢人"或"退出"，查找踢人投票或退出投票
        else if (message.title?.includes('踢人') || message.title?.includes('退出')) {
          console.log('消息包含"踢人"或"退出"，查找投票');
          currentVote = activeVotes.find(vote => 
            vote.voteType === 1 || vote.voteTitle?.includes('踢人') || vote.voteTitle?.includes('退出')
          );
          console.log('找到的投票:', currentVote);
        }
        // 如果消息标题包含"选举"或"新队长"，查找选举投票
        else if (message.title?.includes('选举') || message.title?.includes('新队长')) {
          console.log('消息包含"选举"或"新队长"，查找选举投票');
          currentVote = activeVotes.find(vote => 
            vote.voteType === 3 || vote.voteTitle?.includes('选举') || vote.voteTitle?.includes('新队长')
          );
          console.log('找到的选举投票:', currentVote);
        }
        
        // 如果还没找到，尝试通用查找
        if (!currentVote) {
          console.log('未找到匹配的投票，使用通用查找');
          currentVote = activeVotes.find(vote => 
            vote.voteType === 1 || vote.voteType === 2 || vote.voteType === 3 ||
            vote.voteTitle?.includes('弹劾') || vote.voteTitle?.includes('踢人') || 
            vote.voteTitle?.includes('退出') || vote.voteTitle?.includes('选举') || vote.voteTitle?.includes('新队长')
          );
          console.log('通用查找结果:', currentVote);
        }
        
        console.log('当前投票信息:', currentVote);
        
        // 如果没有找到投票，尝试其他方式
        if (!currentVote && activeVotes.length > 0) {
          console.log('没有找到匹配的投票，使用第一个投票:', activeVotes[0]);
          const firstVote = activeVotes[0];
          voteData.value = {
            task: taskData,
            message: message,
            vote: firstVote,
            teamInfo: teamData
          };
        } else {
          voteData.value = {
            task: taskData,
            message: message,
            vote: currentVote,
            teamInfo: teamData
          };
        }
        
        showVoteModalFlag.value = true;
        console.log('投票界面状态已设置为显示，showVoteModalFlag:', showVoteModalFlag.value);
        console.log('最终投票数据:', voteData.value);
        console.log('投票对象详情:', voteData.value?.vote);
        console.log('用户投票状态:', {
          hasVoted: voteData.value?.vote?.hasVoted,
          userVote: voteData.value?.vote?.userVote,
          votedCount: voteData.value?.vote?.votedCount,
          requiredVotes: voteData.value?.vote?.requiredVotes
        });
        
        // 强制更新DOM
        await nextTick();
        console.log('DOM更新完成');
      } else {
        console.log('任务详情获取失败:', taskResponse.data);
        uni.showToast({ title: '获取任务详情失败', icon: 'none' });
      }
    } catch (apiError) {
      console.log('API调用失败:', apiError);
      uni.showToast({ title: '获取投票信息失败', icon: 'none' });
    }
  } catch (error) {
    console.error('获取投票信息失败:', error);
    uni.showToast({ title: '获取投票信息失败', icon: 'none' });
  } finally {
    voteLoading.value = false;
  }
};

// 关闭投票模态框
const closeVoteModal = () => {
  showVoteModalFlag.value = false;
  currentVoteMessage.value = null;
  voteData.value = null;
  selectedCandidate.value = null; // 重置候选人选择
};

// 重新加载当前消息的投票数据（用于兜底/重试）
const reloadVoteData = async () => {
  try {
    const message = currentVoteMessage.value || voteData.value?.message;
    if (!message?.taskId) return false;
    const token = uni.getStorageSync('accessToken');
    let userId = null;
    try {
      const userStore = useUserStore();
      userId = userStore?.profile?.uid || uni.getStorageSync('userInfo')?.uid || null;
    } catch (_) {}
    if (!userId) return false;
    const teamResponse = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/team/info?taskId=${message.taskId}&userId=${userId}`,
      method: 'GET',
      header: { 'Access-Token': `Bearer ${token}` }
    });
    if (teamResponse.statusCode !== 200 || !teamResponse.data?.success) return false;
    const teamData = teamResponse.data.teamInfo || {};
    const activeVotes = teamData.activeVotes || [];
    if (activeVotes.length === 0) return false;
    const picked = activeVotes.find(v => (v.voteTitle || '').includes('投票')) || activeVotes[0];
    voteData.value = { ...(voteData.value || {}), message, teamInfo: teamData, vote: picked };
    return true;
  } catch (e) {
    console.error('reloadVoteData 失败:', e);
    return false;
  }
};

// 显示任务修改申请弹窗
const openTaskModificationModal = async (message) => {
  try {
    console.log('显示任务修改申请弹窗，消息:', message);
    currentTaskModificationMessage.value = message;
    
    // 解析修改数据
    if (message.extraData) {
      taskModificationData.value = JSON.parse(message.extraData);
      console.log('任务修改数据:', taskModificationData.value);
      
      // 获取铭牌信息（这里需要调用后端API获取修改后的铭牌分配）
      await loadTaskModificationBadges(message.taskId);
    }
    
    showTaskModificationModal.value = true;
  } catch (error) {
    console.error('显示任务修改申请弹窗失败:', error);
    uni.showToast({ title: '加载失败', icon: 'none' });
  }
};

// 关闭任务修改申请弹窗
const closeTaskModificationModal = () => {
  showTaskModificationModal.value = false;
  currentTaskModificationMessage.value = null;
  taskModificationData.value = null;
  taskModificationBadges.value = [];
};

// 加载任务修改后的铭牌信息
const loadTaskModificationBadges = async (taskId) => {
  try {
    const token = uni.getStorageSync('accessToken');
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/task/modification-badges?taskId=${taskId}`,
      method: 'GET',
      header: { 'Access-Token': `Bearer ${token}` }
    });
    
    if (response.data && response.data.success) {
      taskModificationBadges.value = response.data.data || [];
    }
  } catch (error) {
    console.error('加载铭牌信息失败:', error);
  }
};

// 同意任务修改申请
const approveTaskModification = async () => {
  try {
    const token = uni.getStorageSync('accessToken');
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/task/vote-modification`,
      method: 'POST',
      data: {
        taskId: currentTaskModificationMessage.value.taskId,
        approve: true
      },
      header: { 'Access-Token': `Bearer ${token}` }
    });
    
    if (response.data && response.data.success) {
      uni.showToast({ title: '已同意修改申请', icon: 'success' });
      closeTaskModificationModal();
      // 刷新消息列表
      loadMessages();
    } else {
      uni.showToast({ title: response.data?.message || '操作失败', icon: 'none' });
    }
  } catch (error) {
    console.error('同意修改申请失败:', error);
    uni.showToast({ title: '操作失败', icon: 'none' });
  }
};

// 拒绝任务修改申请
const rejectTaskModification = async () => {
  try {
    const token = uni.getStorageSync('accessToken');
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/task/vote-modification`,
      method: 'POST',
      data: {
        taskId: currentTaskModificationMessage.value.taskId,
        approve: false
      },
      header: { 'Access-Token': `Bearer ${token}` }
    });
    
    if (response.data && response.data.success) {
      uni.showToast({ title: '已拒绝修改申请', icon: 'success' });
      closeTaskModificationModal();
      // 刷新消息列表
      loadMessages();
    } else {
      uni.showToast({ title: response.data?.message || '操作失败', icon: 'none' });
    }
  } catch (error) {
    console.error('拒绝修改申请失败:', error);
    uni.showToast({ title: '操作失败', icon: 'none' });
  }
};

// 参与投票（使用新的 /message/vote 接口）
const submitVote = async (voteChoice) => {
  try {
    // 如果选择弃权，直接关闭对话框
    if (voteChoice === 'abstain') {
      console.log('用户选择弃权，关闭投票对话框');
      uni.showToast({ 
        title: '已选择不参与投票', 
        icon: 'none',
        duration: 1500
      });
      closeVoteModal();
      return;
    }
    
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }
    
    // 获取 messageId（优先从 voteData.vote.messageId 获取，其次从 message.id 获取）
    let messageId = voteData.value?.vote?.messageId || currentVoteMessage.value?.id || voteData.value?.message?.id;
    
    if (!messageId) {
      // 尝试重新加载投票数据获取 messageId
      const ok = await reloadVoteData();
      messageId = voteData.value?.vote?.messageId || currentVoteMessage.value?.id || voteData.value?.message?.id;
      if (!ok || !messageId) {
        uni.showToast({ title: '投票消息已失效，请刷新后重试', icon: 'none' });
        return;
      }
    }
    
    console.log('提交投票，messageId:', messageId, 'voteChoice:', voteChoice);
    
    // 转换投票选择为数字格式
    let voteChoiceNum;
    if (voteChoice === 'approve') {
      voteChoiceNum = 1; // 同意
    } else if (voteChoice === 'reject') {
      voteChoiceNum = 2; // 反对
    } else {
      voteChoiceNum = null;
    }
    
    if (!voteChoiceNum) {
      uni.showToast({ title: '无效的投票选择', icon: 'none' });
      return;
    }
    
    console.log('转换后的投票参数:', {
      messageId,
      voteChoice: voteChoiceNum,
      voteReason: ''
    });
    
    voteLoading.value = true;
    
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/vote`,
      method: 'POST',
      data: {
        messageId: Number(messageId),
        voteChoice: voteChoiceNum,
        voteReason: '',
        candidateId: null // 普通投票不需要候选人ID
      },
      header: {
        'Access-Token': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    // 解析响应数据
    let responseData;
    if (typeof response.data === 'string') {
      try {
        responseData = JSON.parse(response.data);
      } catch (e) {
        console.error('解析响应数据失败:', e);
        responseData = { success: false, message: '响应数据格式错误' };
      }
    } else {
      responseData = response.data;
    }
    
    console.log('投票响应:', responseData);
    
    if (responseData && responseData.success) {
      uni.showToast({ 
        title: voteChoice === 'approve' ? '已投票同意' : '已投票反对', 
        icon: 'success' 
      });
      closeVoteModal();
      // 刷新消息列表
      await fetchMessages();
    } else {
      uni.showToast({ title: responseData?.message || '投票失败', icon: 'none' });
    }
  } catch (error) {
    console.error('投票失败:', error);
    uni.showToast({ title: '投票失败，请重试', icon: 'none' });
  } finally {
    voteLoading.value = false;
  }
};

// 撤回投票（使用新的 /message/withdraw-vote 接口）
const withdrawVote = async () => {
  try {
    withdrawLoading.value = true;
    
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }
    
    // 获取 messageId（优先从 voteData.vote.messageId 获取，其次从 message.id 获取）
    let messageId = voteData.value?.vote?.messageId || currentVoteMessage.value?.id || voteData.value?.message?.id;
    
    if (!messageId) {
      uni.showToast({ title: '投票消息已失效，请刷新后重试', icon: 'none' });
      return;
    }
    
    console.log('撤回投票，messageId:', messageId);
    
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/withdraw-vote`,
      method: 'POST',
      data: {
        messageId: Number(messageId)
      },
      header: {
        'Access-Token': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    // 解析响应数据
    let responseData;
    if (typeof response.data === 'string') {
      try {
        responseData = JSON.parse(response.data);
      } catch (e) {
        console.error('解析响应数据失败:', e);
        responseData = { success: false, message: '响应数据格式错误' };
      }
    } else {
      responseData = response.data;
    }
    
    console.log('撤回投票响应:', responseData);
    
    if (responseData && responseData.success) {
      uni.showToast({ 
        title: '投票已撤回', 
        icon: 'success' 
      });
      
      // 重新获取投票数据
      await showVoteModal(currentVoteMessage.value);
    } else {
      uni.showToast({ title: responseData?.message || '撤回失败', icon: 'none' });
    }
  } catch (error) {
    console.error('撤回投票失败:', error);
    uni.showToast({ title: '撤回失败，请重试', icon: 'none' });
  } finally {
    withdrawLoading.value = false;
  }
};

// 跳转到任务详情页
const goToTaskDetail = () => {
  if (voteData.value && voteData.value.task) {
    uni.navigateTo({
      url: `/pages/task/detail/index?id=${voteData.value.task.taskId}`
    });
  }
};

// 格式化数字
const formatNumber = (num) => {
  if (num === null || num === undefined) return '0';
  return Number(num).toLocaleString();
};

// 获取投票规则文本（使用后端返回的数据）
const getVoteRuleText = (vote) => {
  if (!vote) return '';
  
  if (vote.voteType === 2) { // 弹劾投票
    return `弹劾需要${vote.requiredVotes || 0}票（${vote.totalVoters || 0}人中的≥2/3）`;
  } else if (vote.voteType === 1) { // 踢人投票
    return `踢人需要${vote.requiredVotes || 0}票（${vote.totalVoters || 0}人中的50%及以上）`;
  } else if (vote.voteType === 3) { // 选举投票
    return `选举需要${vote.totalVoters || 0}人中的50%以上参与投票（不管投给谁）`;
  }
  
  return '';
};

// 获取投票类型文本
const getVoteTypeText = (voteType) => {
  switch (voteType) {
    case 1:
      return '踢人投票';
    case 2:
      return '弹劾投票';
    case 3:
      return '选举新队长投票';
    default:
      return '未知投票类型';
  }
};

// 获取候选人列表（排除当前队长）
const getCandidates = () => {
  console.log('getCandidates 被调用');
  console.log('voteData.value:', voteData.value);
  console.log('teamInfo:', voteData.value?.teamInfo);
  console.log('members:', voteData.value?.teamInfo?.members);
  
  if (!voteData.value?.teamInfo?.members) {
    console.log('没有团队成员数据');
    return [];
  }
  
  const currentLeaderId = voteData.value.teamInfo.leaderId;
  console.log('当前队长ID:', currentLeaderId);
  
  const candidates = voteData.value.teamInfo.members.filter(member => {
    const isNotLeader = member.userId !== currentLeaderId;
    const isConfirmed = member.status === 'confirmed' || member.status === 1;
    console.log(`成员 ${member.userId}: 不是队长=${isNotLeader}, 已确认=${isConfirmed}, 状态=${member.status}`);
    return isNotLeader && isConfirmed;
  });
  
  console.log('过滤后的候选人:', candidates);
  return candidates;
};

// 选择候选人
const selectCandidate = (userId) => {
  selectedCandidate.value = userId;
  console.log('选择候选人:', userId);
};

// 提交候选人投票（使用新的 /message/vote 接口）
const submitCandidateVote = async () => {
  if (!selectedCandidate.value) {
    uni.showToast({ title: '请先选择候选人', icon: 'none' });
    return;
  }
  
  try {
    voteLoading.value = true;
    
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }
    
    // 获取 messageId（优先从 voteData.vote.messageId 获取，其次从 message.id 获取）
    let messageId = voteData.value?.vote?.messageId || currentVoteMessage.value?.id || voteData.value?.message?.id;
    
    if (!messageId) {
      // 尝试重新加载投票数据获取 messageId
      const ok = await reloadVoteData();
      messageId = voteData.value?.vote?.messageId || currentVoteMessage.value?.id || voteData.value?.message?.id;
      if (!ok || !messageId) {
        uni.showToast({ title: '投票消息已失效，请刷新后重试', icon: 'none' });
        return;
      }
    }
    
    console.log('提交候选人投票，messageId:', messageId, 'candidateId:', selectedCandidate.value);
    
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/vote`,
      method: 'POST',
      data: {
        messageId: Number(messageId),
        voteChoice: 1, // 选举投票统一为同意
        voteReason: `选择 ${getUserNickname(selectedCandidate.value)} 为新队长`,
        candidateId: selectedCandidate.value // 候选人ID
      },
      header: {
        'Access-Token': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    // 解析响应数据
    let responseData;
    if (typeof response.data === 'string') {
      try {
        responseData = JSON.parse(response.data);
      } catch (e) {
        console.error('解析响应数据失败:', e);
        responseData = { success: false, message: '响应数据格式错误' };
      }
    } else {
      responseData = response.data;
    }
    
    console.log('候选人投票响应:', responseData);
    
    if (responseData && responseData.success) {
      uni.showToast({ 
        title: `已选择 ${getUserNickname(selectedCandidate.value)} 为新队长`, 
        icon: 'success' 
      });
      closeVoteModal();
      // 刷新消息列表
      await fetchMessages();
    } else {
      uni.showToast({ title: responseData?.message || '投票失败', icon: 'none' });
    }
  } catch (error) {
    console.error('候选人投票失败:', error);
    uni.showToast({ title: '投票失败，请重试', icon: 'none' });
  } finally {
    voteLoading.value = false;
  }
};


// 获取任务状态文本
const getStatusText = (status) => {
  const statusMap = {
    '0': '待挑战',
    '1': '进行中', 
    '2': '进行中',
    '3': '已完成',
    '4': '已取消',
    '5': '已过期'
  };
  return statusMap[status] || '未知状态';
};

const tabRefs = ref([]);
const underlineStyle = ref({});

// 状态筛选兼容picker
const statusOptions = ['全部状态', '待处理', '已处理', '已同意', '已拒绝', '已撤回'];
const statusIndex = ref(0);
const categoryIndex = ref(0); // 对应系统通知
function onStatusPickerChange(e) {
  statusIndex.value = e.detail.value;
  if (statusIndex.value == 0) status.value = '';
  else if (statusIndex.value == 1) status.value = '0';  // 待处理/未读/待投票
  else if (statusIndex.value == 2) status.value = '1';  // 已处理/已读/已投同意
  else if (statusIndex.value == 3) status.value = '2';  // 已投拒绝
  else if (statusIndex.value == 4) status.value = '4';  // 已撤回
  else if (statusIndex.value == 5) status.value = '5';  // 已结束
  applyFilter();
}

// 分类筛选处理
function onCategoryChange() {
  type.value = ''; // 切换分类时清空类型筛选
  applyFilter();
}

function onCategoryPickerChange(e) {
  categoryIndex.value = e.detail.value;
  category.value = categoryOptions[categoryIndex.value].value;
  type.value = ''; // 切换分类时清空类型筛选
  applyFilter();
}
watch(status, (val) => {
  if (val === '') statusIndex.value = 0;
  else if (val === '0') statusIndex.value = 1;
  else if (val === '1') statusIndex.value = 2;
  else if (val === '2') statusIndex.value = 3;
  else if (val === '3') statusIndex.value = 4;
  else if (val === '4') statusIndex.value = 5;
});
const fetchMessages = async () => {
  loading.value = true;
  error.value = '';
  try {
    const token = uni.getStorageSync('accessToken');
    const params = { page: page.value, size: size.value };
    
    // 添加分类筛选参数（现在category总是有值）
    params.category = category.value;
    if (status.value !== '') params.status = Number(status.value);
    if (type.value !== '') params.type = Number(type.value);
    
    console.log('请求参数:', params);
    
    const res = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/list`,
      method: 'GET',
      header: { 'Access-Token': `Bearer ${token}` },
      data: params
    });
    
    messages.value = res.data;
    console.log('获取到的消息数量:', messages.value.content.length);

    // 初始化/更新倒计时映射（基于后端 remainingSeconds 或过期时间）
    (messages.value?.content || []).forEach(m => {
      if (!m.id) return;
      
      // 优先使用后端返回的 remainingSeconds（如果有）
      const sec = typeof m.remainingSeconds === 'number' ? m.remainingSeconds : null;
      if (sec != null) {
        msgCountdownMap[m.id] = Math.max(0, sec);
      } else {
        // 如果没有 remainingSeconds，尝试从过期时间计算
        // 只计算未投票（status === 0）且未过期（有 expiresAt）的消息
        const expiresAt = getExpiresAt(m);
        if (expiresAt && m.status === 0) { // status === 0 表示待投票/未投票
          try {
            // 调试：打印实际收到的时间格式
            console.log(`[倒计时] 消息ID=${m.id}, expiresAt类型=${typeof expiresAt}, 值=${expiresAt}`);
            console.log(`[倒计时] 消息对象:`, m);
            
            let expiresMs = null;
            if (typeof expiresAt === 'number') {
              expiresMs = expiresAt < 1e12 ? expiresAt * 1000 : expiresAt;
            } else if (typeof expiresAt === 'string') {
              const trimmed = expiresAt.trim();
              if (/^\d+$/.test(trimmed)) {
                const num = Number(trimmed);
                expiresMs = num < 1e12 ? num * 1000 : num;
              } else {
                // 后端返回的格式可能是 "yyyy-MM-dd HH:mm:ss" 或 "yyyy-MM-ddTHH:mm:ss"
                const looksISO = trimmed.includes('T');
                const hasZone = /[zZ]|[+\-]\d{2}:?\d{2}$/.test(trimmed);
                
                console.log(`[倒计时] 解析时间字符串: "${trimmed}", looksISO=${looksISO}, hasZone=${hasZone}`);
                
                if (looksISO && !hasZone) {
                  // ISO格式但没有时区：可能是 "2025-01-15T14:30:00"
                  // 尝试按本地时间解析（假设服务器和客户端同一时区）
                  const parsed = Date.parse(trimmed);
                  if (!isNaN(parsed)) {
                    expiresMs = parsed;
                    console.log(`[倒计时] 解析成功(ISO无时区): ${expiresMs}`);
                  }
                } else if (!looksISO && !hasZone) {
                  // 格式为 "yyyy-MM-dd HH:mm:ss"
                  // 尝试两种方式：1. 按本地时间解析 2. 按UTC+8解析
                  const localParsed = Date.parse(trimmed.replace(' ', 'T'));
                  const utc8Parsed = Date.parse(trimmed.replace(' ', 'T') + '+08:00');
                  
                  console.log(`[倒计时] 本地解析: ${localParsed}, UTC+8解析: ${utc8Parsed}, 当前时间: ${Date.now()}`);
                  
                  // 选择更合理的时间（不应该太远）
                  const now = Date.now();
                  if (!isNaN(localParsed) && Math.abs(localParsed - now) < Math.abs((utc8Parsed || 0) - now)) {
                    expiresMs = localParsed;
                    console.log(`[倒计时] 使用本地时间解析: ${expiresMs}`);
                  } else if (!isNaN(utc8Parsed)) {
                    expiresMs = utc8Parsed;
                    console.log(`[倒计时] 使用UTC+8解析: ${expiresMs}`);
                  } else if (!isNaN(localParsed)) {
                    expiresMs = localParsed;
                    console.log(`[倒计时] 备用：使用本地时间: ${expiresMs}`);
                  }
                } else {
                  // 有时区信息，直接解析
                  const parsed = Date.parse(trimmed);
                  if (!isNaN(parsed)) {
                    expiresMs = parsed;
                    console.log(`[倒计时] 解析成功(有时区): ${expiresMs}`);
                  }
                }
              }
            } else if (expiresAt instanceof Date) {
              expiresMs = expiresAt.getTime();
            }
            
            if (expiresMs) {
              const remainingMs = expiresMs - Date.now();
              const remainingSec = Math.floor(remainingMs / 1000);
              console.log(`[倒计时] 消息ID=${m.id}, 剩余秒数=${remainingSec}`);
              // 只存储还有剩余时间的倒计时（大于0）
              if (remainingSec > 0) {
                msgCountdownMap[m.id] = remainingSec;
              } else {
                console.log(`[倒计时] 消息ID=${m.id} 已过期，剩余秒数=${remainingSec}`);
                // 如果已过期，可能需要更新状态（但这里不更新，由后端定时任务处理）
              }
            } else {
              console.warn(`[倒计时] 消息ID=${m.id} 无法解析过期时间: ${expiresAt}`);
            }
          } catch (e) {
            console.error('计算投票消息倒计时失败:', e, e.stack);
          }
        }
      }
    });
    // 开启全局唯一的 setInterval
    if (!msgCountdownTimer) {
      msgCountdownTimer = setInterval(() => {
        const now = Date.now();
        Object.keys(msgCountdownMap).forEach(k => {
          const current = msgCountdownMap[k] || 0;
          if (current > 0) {
            msgCountdownMap[k] = current - 1;
            // 倒计时到0时，检查是否需要更新状态或刷新
            if (msgCountdownMap[k] === 0) {
              console.log(`[倒计时] 消息ID=${k} 倒计时已到0`);
              // 找到对应的消息，检查是否真的过期
              const msg = messages.value?.content?.find(m => m.id == k);
              if (msg && msg.status === 0) {
                // 如果状态还是待投票，可能需要刷新（后端定时任务应该会更新状态）
                // 延迟1秒后刷新，给后端一些时间处理
                setTimeout(() => {
                  const stillPending = messages.value?.content?.find(m => m.id == k && m.status === 0);
                  if (stillPending) {
                    console.log(`[倒计时] 消息ID=${k} 倒计时到0但状态仍为待投票，刷新消息列表`);
                    // 可以触发刷新，但为了不过于频繁，先不自动刷新
                    // fetchMessages();
                  }
                }, 1000);
              }
            }
          }
        });
      }, 1000);
    }
  } catch (e) {
    error.value = e.response?.data?.message || e.message || '请求失败';
  } finally {
    loading.value = false;
  }
};

// 页面销毁时清理定时器
onBeforeUnmount(() => {
  if (msgCountdownTimer) {
    clearInterval(msgCountdownTimer);
    msgCountdownTimer = null;
  }
});

const applyFilter = () => {
  page.value = 0;
  fetchMessages();
};

const resetFilters = () => {
  type.value = '';
  status.value = "";
  category.value = 'system'; // 重置为系统消息
  statusIndex.value = 0;
  categoryIndex.value = 0;
  
  // 重置状态选项
  currentStatusOptions.value = statusOptionsConfig['system'] || [];
  
  page.value = 0;
  fetchMessages();
};

const changePage = (p) => {
  page.value = p;
  fetchMessages();
};

// 注入测试消息模块（已关闭）。如需启用，取消上方按钮与本函数的注释。
/*
const injectMockOrgApply = () => {
  const now = new Date().toISOString().slice(0,19).replace('T',' ');
  const mock = { id: Date.now(), title: '企业入驻申请', content: '用户 张三(学生) 申请加入 安徽师范大学', type: 1, status: 0, createTime: now,
    extraData: JSON.stringify({ action: 'membership_application', applicantId: '20001', orgCreditCode: '123400004660031000', applicantName: '张三', applicantRole: '学生' }) };
  const list = messages.value?.content ? [...messages.value.content] : [];
  list.unshift(mock);
  messages.value = { ...(messages.value || {}), content: list, totalElements: (messages.value?.totalElements || 0) + 1 };
  uni.showToast({ title: '已注入测试入驻消息', icon: 'none' });
};
*/

// 其它弹窗、操作逻辑保留
const markAsRead = async (id, category = null) => {
  console.log('markAsRead 被调用，id:', id, 'category:', category);
  try {
    const token = uni.getStorageSync('accessToken');
    let url = `${NETWORK_CONFIG.API_BASE_URL}/message/read?id=${id}`;
    if (category) {
      url += `&category=${category}`;
    }
    await uni.request({
      url: url,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` }
    });
    actionMsg.value = `消息 ${id} 已标记为已读`;
    fetchMessages();
    // 刷新未读数量，更新tabBar红点
    fetchUnreadCounts();
  } catch (e) {
    console.error('markAsRead 请求出错:', e);
    actionMsg.value = e.response?.data?.message || e.message || '操作失败';
  }
};

// 一键已读所有系统消息
const markAllSystemMessagesRead = async () => {
  try {
    // 先确认操作
    const result = await uni.showModal({
      title: '确认操作',
      content: '确定要将所有系统消息标记为已读吗？',
      showCancel: true,
      confirmText: '确定',
      cancelText: '取消'
    });
    
    if (!result.confirm) {
      return;
    }
    
    uni.showLoading({ title: '处理中...' });
    
    const token = uni.getStorageSync('accessToken');
    
    // 获取当前所有未读的系统消息
    const unreadMessages = messages.value.content.filter(msg => msg.status === 0);
    
    if (unreadMessages.length === 0) {
      uni.hideLoading();
      uni.showToast({ title: '没有未读消息', icon: 'none' });
      return;
    }
    
    // 批量标记为已读
    let successCount = 0;
    for (const msg of unreadMessages) {
      try {
        await uni.request({
          url: `${NETWORK_CONFIG.API_BASE_URL}/message/read?id=${msg.id}`,
          method: 'POST',
          header: { 'Access-Token': `Bearer ${token}` }
        });
        successCount++;
      } catch (e) {
        console.error(`标记消息 ${msg.id} 为已读失败:`, e);
      }
    }
    
    uni.hideLoading();
    
    if (successCount > 0) {
      uni.showToast({ 
        title: `已标记 ${successCount} 条消息为已读`, 
        icon: 'success',
        duration: 2000
      });
      
      // 刷新消息列表和未读计数
      await fetchMessages();
      await fetchUnreadCounts();
    } else {
      uni.showToast({ title: '操作失败', icon: 'none' });
    }
  } catch (e) {
    uni.hideLoading();
    console.error('批量标记已读失败:', e);
    uni.showToast({ title: '操作失败，请重试', icon: 'none' });
  }
};
const handleTaskCompletion = async (messageId, approve) => {
  try {
    const token = uni.getStorageSync('accessToken');
    const reason = !approve ? (reasonText.value || '') : (reasonText.value || '');
    const res = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/handle-task-completion?id=${messageId}&approve=${approve}&proof=${encodeURIComponent(reason)}`,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` }
    });
    actionMsg.value = (res.data && res.data.message) || (approve ? '已同意任务完成' : '已拒绝任务完成');
    showReasonDialog.value = false;
    reasonText.value = '';
    fetchMessages();
  } catch (e) {
    uni.showToast({ title: e.response?.data || e.message || '操作失败', icon: 'none' });
  }
};

const handleTaskApply = async (id, approve) => {
  try {
    const token = uni.getStorageSync('accessToken');
    const reason = !approve ? (reasonText.value || '') : '';
    const res = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/handle-task-apply?id=${id}&approve=${approve}&reason=${encodeURIComponent(reason)}`,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` }
    });
    actionMsg.value = (res.data && res.data.message) || (approve ? '已同意任务申请' : '已拒绝任务申请');
    showReasonDialog.value = false;
    reasonText.value = '';
    fetchMessages();
    // 刷新未读数量，更新tabBar红点
    fetchUnreadCounts();
  } catch (e) {
    uni.showToast({ title: e.response?.data || e.message || '操作失败', icon: 'none' });
  }
};
// 处理企业入驻申请
const handleOrgApply = async (id, approve) => {
  try {
    const token = uni.getStorageSync('accessToken');
    const reason = !approve ? (reasonText.value || '') : '';
    const res = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/handle-org-apply?id=${id}&approve=${approve}&reason=${encodeURIComponent(reason)}`,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` }
    });
    actionMsg.value = (res.data && res.data.message) || (approve ? '已同意企业入驻申请' : '已拒绝企业入驻申请');
    showReasonDialog.value = false;
    reasonText.value = '';
    fetchMessages();
    // 刷新未读数量，更新tabBar红点
    fetchUnreadCounts();
  } catch (e) {
    uni.showToast({ title: e.response?.data || e.message || '操作失败', icon: 'none' });
  }
};

// 处理任务取消申请
const handleTaskCancelRequest = async (messageId, approve) => {
  try {
    uni.showLoading({ title: '处理中...' });
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }
    
    // 从消息中提取任务ID
    const message = messages.value.content.find(msg => msg.id === messageId);
    if (!message || !message.taskId) {
      throw new Error('无法获取任务信息');
    }
    
    const url = `${NETWORK_CONFIG.API_BASE_URL}/task/handle-cancel-request?taskId=${message.taskId}&approve=${approve}`;
    const data = { reason: reasonText.value };
    
    const response = await uni.request({
      url,
      method: 'POST',
      data,
      header: {
        'Content-Type': 'application/json',
        'Access-Token': `Bearer ${token}`
      }
    });
    
    if (response.statusCode === 200) {
      uni.hideLoading();
      uni.showToast({ 
        title: approve ? '已同意取消任务' : '已拒绝取消任务', 
        icon: 'success' 
      });
      await fetchMessages(); // 重新加载消息列表
      
      // 发送全局事件通知任务详情页面刷新
      if (approve) {
        uni.$emit('task-cancel-approved', {
          taskId: message.taskId,
          message: '任务取消申请已同意，任务状态已更新'
        });
      }
    } else {
      throw new Error(response.data?.message || '处理失败');
    }
  } catch (error) {
    uni.hideLoading();
    uni.showToast({ title: error.message || '处理失败', icon: 'none' });
  } finally {
    showReasonDialog.value = false;
    reasonText.value = '';
    currentAction.value = {};
  }
};
const handleApprove = async () => {
  if (!currentAction.value || !currentAction.value.id) return;
  const { id, type } = currentAction.value;
  
  if (type === 'org-apply') {
    await handleOrgApply(id, true);
  } else if (type === 'task-apply') {
    await handleTaskApply(id, true);
  } else if (type === 'task-completion') {
    await handleTaskCompletion(id, true);
  } else if (type === 'task-cancel-request') {
    await handleTaskCancelRequest(id, true);
  }
};

const handleReject = async () => {
  if (!currentAction.value || !currentAction.value.id) return;
  const { id, type } = currentAction.value;
  
  if (type === 'org-apply') {
    await handleOrgApply(id, false);
  } else if (type === 'task-apply') {
    await handleTaskApply(id, false);
  } else if (type === 'task-completion') {
    await handleTaskCompletion(id, false);
  } else if (type === 'task-cancel-request') {
    await handleTaskCancelRequest(id, false);
  }
};

const confirmAction = async () => {
  if (!currentAction.value || !currentAction.value.id) return;
  const { id, type, approve } = currentAction.value;
  
  if (type === 'org-apply') {
    await handleOrgApply(id, approve);
  } else if (type === 'task-apply') {
    await handleTaskApply(id, approve);
  } else if (type === 'task-completion') {
    await handleTaskCompletion(id, approve);
  } else if (type === 'task-cancel-request') {
    await handleTaskCancelRequest(id, approve);
  }
};
const cancelAction = () => {
  showReasonDialog.value = false;
  reasonText.value = '';
  currentAction.value = {};
};

// 获取用户信息
const getUserInfo = async (userId) => {
  if (userInfoCache.value[userId]) {
    return userInfoCache.value[userId];
  }
  
  try {
    const token = uni.getStorageSync('accessToken');
    if (!token) return null;
    
    console.log('获取用户信息，userId:', userId);
    
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/user/info`,
      method: 'POST',
      data: {
        uid: userId
      },
      header: {
        'Access-Token': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    console.log('用户信息响应:', response);
    
    if (response.statusCode === 200 && response.data) {
      const userInfo = response.data;
      userInfoCache.value[userId] = userInfo;
      console.log('用户信息缓存成功:', userInfo);
      return userInfo;
    } else {
      console.error('获取用户信息失败，响应:', response.data);
    }
  } catch (error) {
    console.error('获取用户信息失败:', error);
  }
  
  return null;
};

// 获取用户昵称
const getUserNickname = (userId) => {
  const userInfo = userInfoCache.value[userId];
  return userInfo ? userInfo.nickname : '加载中...';
};

// 获取用户头像
const getUserAvatar = (userId) => {
  const userInfo = userInfoCache.value[userId];
  console.log('获取用户头像，userId:', userId, 'userInfo:', userInfo);
  
  if (userInfo && userInfo.avatarUrl) {
    if (userInfo.avatarUrl.startsWith('/')) {
      const fullUrl = `${NETWORK_CONFIG.API_BASE_URL}${userInfo.avatarUrl}`;
      console.log('头像URL（相对路径）:', fullUrl);
      return fullUrl;
    }
    console.log('头像URL（绝对路径）:', userInfo.avatarUrl);
    return userInfo.avatarUrl;
  }
  
  console.log('使用默认头像');
  return '/static/images/default-avatar.png';
};

// 头像加载错误处理
const handleAvatarError = (e) => {
  console.log('头像加载失败:', e);
  e.target.src = '/static/images/default-avatar.png';
};

// 检查工作核验状态（从消息extraData中获取）
const getVerificationStatus = (msg) => {
  try {
    const workVerification = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    console.log('getVerificationStatus - workVerification:', workVerification);
    console.log('getVerificationStatus - result:', workVerification.result, '类型:', typeof workVerification.result);
    return workVerification.result; // 0=失败, 1=成功, null/undefined=未核验
  } catch (e) {
    console.error('getVerificationStatus 解析错误:', e);
    return null;
  }
};

// 获取核验状态文本
const getVerificationStatusText = (msg) => {
  const status = getVerificationStatus(msg);
  console.log('getVerificationStatusText - status:', status, '严格等于0:', status === 0, '严格等于1:', status === 1);
  if (status === 1) return '已处理';
  if (status === 0) return '未处理';
  if (status === 2) return '已过期';
  return '未核验';
};

// 获取核验状态样式类
const getVerificationStatusClass = (msg) => {
  const status = getVerificationStatus(msg);
  if (status === 1) return 'status-processed';
  if (status === 0) return 'status-pending';
  if (status === 2) return 'status-expired';
  return 'status-pending';
};

// AI远程提问相关函数
const getExamScheduledTime = (msg) => {
  try {
    const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    return extraData.scheduledStartTime;
  } catch (e) {
    return null;
  }
};

const getExamStatusText = (msg) => {
  try {
    const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    const status = extraData.status;
    if (status === 1) return '已预约';
    if (status === 2) return '进行中';
    if (status === 3) return '已完成';
    if (status === 4) return '异常';
    return '待预约';
  } catch (e) {
    return '待预约';
  }
};

const getExamStatusClass = (msg) => {
  try {
    const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    const status = extraData.status;
    if (status === 'scheduled' || status === 1) return 'status-scheduled';
    if (status === 'in_progress' || status === 2) return 'status-in-progress';
    if (status === 'completed' || status === 3) return 'status-completed';
    if (status === 4) return 'status-abnormal';
    return 'status-pending';
  } catch (e) {
    return 'status-pending';
  }
};

const formatScheduledTime = (scheduledTime) => {
  if (!scheduledTime) return '-';
  try {
    const date = new Date(scheduledTime);
    return date.toLocaleString('zh-CN', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit'
    });
  } catch (e) {
    return scheduledTime;
  }
};

// 团队邀请消息信息解析
const getTeamInvitationInfo = (msg) => {
  try {
    const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    return {
      leaderNickname: extraData.leaderNickname,
      leaderAvatar: extraData.leaderAvatar,
      teamName: extraData.teamName,
      taskTitle: extraData.taskTitle,
      taskContent: extraData.taskContent,
      taskId: msg.taskId || extraData.taskId,
      teamId: msg.teamId || extraData.teamId
    };
  } catch (e) {
    console.error('解析团队邀请信息失败:', e);
    return null;
  }
};

// 跳转到任务页面
const jumpToTask = (taskId) => {
  if (taskId) {
    console.log('跳转到任务页面，任务ID:', taskId);
    uni.navigateTo({
      url: `/pages/task/detail/index?id=${taskId}`
    });
  } else {
    uni.showToast({ title: '任务信息缺失', icon: 'none' });
  }
};

// 从弹窗跳转到任务页面
const jumpToTaskFromModal = (taskId) => {
  if (taskId) {
    console.log('从弹窗跳转到任务页面，任务ID:', taskId);
    showTeamInvitationModal.value = false; // 关闭弹窗
    uni.navigateTo({
      url: `/pages/task/detail/index?id=${taskId}`
    });
  } else {
    uni.showToast({ title: '任务信息缺失', icon: 'none' });
  }
};

// 处理团队邀请同意
const handleTeamInvitationAccept = async () => {
  if (currentTeamInvitation.value) {
    await handleTeamInvitation(currentTeamInvitation.value.teamId, true);
    showTeamInvitationModal.value = false;
    // 跳转到任务详情页
    if (currentTeamInvitation.value.taskId) {
      uni.navigateTo({
        url: `/pages/task/detail/index?id=${currentTeamInvitation.value.taskId}`
      });
    }
  }
};

// 处理团队邀请拒绝
const handleTeamInvitationReject = async () => {
  if (currentTeamInvitation.value) {
    await handleTeamInvitation(currentTeamInvitation.value.teamId, false);
    showTeamInvitationModal.value = false;
  }
};

// 处理团队邀请暂不处理
const handleTeamInvitationPostpone = async () => {
  if (currentTeamInvitation.value) {
    // 暂不处理只是关闭弹窗，不发送任何请求
    showTeamInvitationModal.value = false;
    uni.showToast({ 
      title: '已暂不处理，可稍后再次查看', 
      icon: 'none',
      duration: 2000
    });
  }
};

// 获取邀请人的用户信息
const fetchUserInfoForInvitation = async (leaderId) => {
  try {
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      console.error('未找到token，无法获取用户信息');
      return;
    }
    
    console.log('获取邀请人信息，leaderId:', leaderId);
    
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/user/info`,
      method: 'POST',
      data: { uid: leaderId },
      header: { 
        'Access-Token': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    console.log('用户信息API响应:', response);
    
    if (response.statusCode === 200 && response.data) {
      const userInfo = response.data;
      console.log('获取到的用户信息:', userInfo);
      
      // 更新弹窗中的用户信息
      if (currentTeamInvitation.value) {
        currentTeamInvitation.value.leaderNickname = userInfo.nickname || '未知用户';
        
        // 处理头像URL，使用完整的URL
        let avatarUrl = userInfo.avatarUrl || '/static/images/default-avatar.png';
        if (avatarUrl.startsWith('/uploads/')) {
          avatarUrl = `${NETWORK_CONFIG.API_BASE_URL}${avatarUrl}`;
        } else if (avatarUrl.startsWith('/')) {
          avatarUrl = `${NETWORK_CONFIG.API_BASE_URL}${avatarUrl}`;
        }
        currentTeamInvitation.value.leaderAvatar = avatarUrl;
        
        console.log('更新后的邀请人信息:', currentTeamInvitation.value);
      }
    } else {
      console.error('获取用户信息失败:', response);
      if (currentTeamInvitation.value) {
        currentTeamInvitation.value.leaderNickname = '获取失败';
      }
    }
  } catch (error) {
    console.error('获取邀请人信息失败:', error);
    if (currentTeamInvitation.value) {
      currentTeamInvitation.value.leaderNickname = '获取失败';
    }
  }
};

// 获取任务详细信息
const fetchTaskInfoForInvitation = async (taskId) => {
  try {
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      console.error('未找到token，无法获取任务信息');
      return;
    }
    
    console.log('获取任务信息，taskId:', taskId);
    
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/task/detail`,
      method: 'GET',
      data: { taskId: taskId },
      header: { 
        'Access-Token': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    console.log('任务信息API响应:', response);
    
    if (response.statusCode === 200 && response.data) {
      const taskInfo = response.data;
      console.log('获取到的任务信息:', taskInfo);
      
      // 更新弹窗中的任务信息
      if (currentTeamInvitation.value) {
        currentTeamInvitation.value.taskTitle = taskInfo.title || '未知任务';
        currentTeamInvitation.value.taskContent = taskInfo.description || '暂无描述';
        currentTeamInvitation.value.taskReward = taskInfo.reward || 0;
        currentTeamInvitation.value.taskDeadline = taskInfo.deadline;
        currentTeamInvitation.value.taskStatus = taskInfo.status;
        currentTeamInvitation.value.taskLocation = taskInfo.location;
        
        console.log('更新后的任务信息:', currentTeamInvitation.value);
      }
    } else {
      console.error('获取任务信息失败:', response);
      if (currentTeamInvitation.value) {
        currentTeamInvitation.value.taskTitle = '获取失败';
        currentTeamInvitation.value.taskContent = '无法获取任务详情';
      }
    }
  } catch (error) {
    console.error('获取任务信息失败:', error);
    if (currentTeamInvitation.value) {
      currentTeamInvitation.value.taskTitle = '获取失败';
      currentTeamInvitation.value.taskContent = '无法获取任务详情';
    }
  }
};

// 格式化任务截止时间
const formatTaskDeadline = (deadline) => {
  if (!deadline) return '未设置';
  
  try {
    const date = new Date(deadline);
    const now = new Date();
    const diffTime = date.getTime() - now.getTime();
    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
    
    if (diffDays < 0) {
      return '已过期';
    } else if (diffDays === 0) {
      return '今天截止';
    } else if (diffDays === 1) {
      return '明天截止';
    } else if (diffDays <= 7) {
      return `${diffDays}天后截止`;
    } else {
      return date.toLocaleDateString('zh-CN', {
        year: 'numeric',
        month: 'short',
        day: 'numeric'
      });
    }
  } catch (error) {
    console.error('格式化截止时间失败:', error);
    return '时间格式错误';
  }
};

// 获取任务状态文本
const getTaskStatusText = (status) => {
  const statusMap = {
    'PENDING': '待开始',
    'IN_PROGRESS': '进行中',
    'COMPLETED': '已完成',
    'CANCELLED': '已取消',
    'EXPIRED': '已过期'
  };
  return statusMap[status] || '未知状态';
};

// 获取任务状态样式类
const getTaskStatusClass = (status) => {
  const classMap = {
    'PENDING': 'status-pending',
    'IN_PROGRESS': 'status-progress',
    'COMPLETED': 'status-completed',
    'CANCELLED': 'status-cancelled',
    'EXPIRED': 'status-expired'
  };
  return classMap[status] || 'status-unknown';
};

const getMinDateTime = () => {
  const now = new Date();
  now.setMinutes(now.getMinutes() + 30); // 至少30分钟后
  // 使用本地时间格式，避免时区问题
  const yearStr = String(now.getFullYear());
  const monthStr = String(now.getMonth() + 1).padStart(2, '0');
  const dayStr = String(now.getDate()).padStart(2, '0');
  const hourStr = String(now.getHours()).padStart(2, '0');
  const minuteStr = String(now.getMinutes()).padStart(2, '0');
  
  const result = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}`;
  console.log('[DEBUG] 最小预约时间:', result);
  return result;
};

const openExamScheduleDialog = (examTaskId, questionCount, messageId) => {
  currentExamInfo.value = { examTaskId, questionCount, messageId };
  scheduledDateTime.value = '';
  initDateTimePicker();
  showExamScheduleDialog.value = true;
};

// 初始化时间选择器数据
const initDateTimePicker = () => {
  const now = new Date();
  const minDate = new Date(now.getTime() + 30 * 60 * 1000); // 30分钟后
  
  // 生成年份选项（当前年份到明年）
  const years = [];
  for (let year = now.getFullYear(); year <= now.getFullYear() + 1; year++) {
    years.push(year + '年');
  }
  
  // 生成月份选项
  const months = [];
  for (let month = 1; month <= 12; month++) {
    months.push(month + '月');
  }
  
  // 生成日期选项（1-31日）
  const days = [];
  for (let day = 1; day <= 31; day++) {
    days.push(day + '日');
  }
  
  // 生成小时选项（0-23时）
  const hours = [];
  for (let hour = 0; hour < 24; hour++) {
    hours.push(hour + '时');
  }
  
  // 生成分钟选项（0-59分，每5分钟一个选项）
  const minutes = [];
  for (let minute = 0; minute < 60; minute += 5) {
    minutes.push(minute + '分');
  }
  
  dateTimePickerRange.value = [years, months, days, hours, minutes];
  
  // 设置默认值（30分钟后）
  const yearIndex = minDate.getFullYear() - now.getFullYear();
  const monthIndex = minDate.getMonth();
  const dayIndex = minDate.getDate() - 1;
  const hourIndex = minDate.getHours();
  // 计算最接近的5分钟间隔
  const minuteIndex = Math.floor(minDate.getMinutes() / 5);
  
  dateTimePickerValue.value = [yearIndex, monthIndex, dayIndex, hourIndex, minuteIndex];
  
  // 更新scheduledDateTime
  updateScheduledDateTime();
};

// 时间选择器变化处理
const onDateTimePickerChange = (e) => {
  dateTimePickerValue.value = e.detail.value;
  updateScheduledDateTime();
};

// 更新预约时间字符串
const updateScheduledDateTime = () => {
  const [yearIndex, monthIndex, dayIndex, hourIndex, minuteIndex] = dateTimePickerValue.value;
  const now = new Date();
  const year = now.getFullYear() + yearIndex;
  const month = monthIndex + 1;
  const day = dayIndex + 1;
  const hour = hourIndex;
  const minute = minuteIndex * 5; // 每5分钟一个间隔
  
  // 直接使用选择的年月日时分，避免Date对象的时区转换
  const yearStr = String(year);
  const monthStr = String(month).padStart(2, '0');
  const dayStr = String(day).padStart(2, '0');
  const hourStr = String(hour).padStart(2, '0');
  const minuteStr = String(minute).padStart(2, '0');
  
  scheduledDateTime.value = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}`;
  
  console.log('[DEBUG] 预约时间设置:', {
    selected: { year, month, day, hour, minute },
    formatted: scheduledDateTime.value
  });
};

// 格式化时间选择器显示
const formatDateTimePickerDisplay = () => {
  if (!scheduledDateTime.value) return '请选择时间';
  
  try {
    const date = new Date(scheduledDateTime.value);
    return date.toLocaleString('zh-CN', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit'
    });
  } catch (e) {
    return '请选择时间';
  }
};

const confirmExamSchedule = async () => {
  if (!scheduledDateTime.value) {
    uni.showToast({ title: '请选择考试时间', icon: 'none' });
    return;
  }
  
  try {
    const token = uni.getStorageSync('accessToken');
    const res = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/exam/task/schedule`,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` },
      data: {
        examTaskId: currentExamInfo.value.examTaskId,
        scheduledStartTime: scheduledDateTime.value,
        questionCount: currentExamInfo.value.questionCount
      }
    });
    
    // 同时检查状态码和响应体中的success字段
    if (res.statusCode === 200 && res.data && res.data.success === true) {
      uni.showToast({ title: '预约成功', icon: 'success' });
      showExamScheduleDialog.value = false;
      fetchMessages(); // 刷新消息列表
    } else {
      throw new Error(res.data?.message || res.data || '预约失败');
    }
  } catch (e) {
    uni.showToast({ title: e.message || '预约失败', icon: 'none' });
  }
};

const cancelExamSchedule = () => {
  showExamScheduleDialog.value = false;
  currentExamInfo.value = { examTaskId: null, questionCount: 0, messageId: null };
  scheduledDateTime.value = '';
  dateTimePickerValue.value = [0, 0, 0, 0];
};


// 工作核验相关函数
const openWorkVerifyDialog = (workVerificationId) => {
  console.log('openWorkVerifyDialog 被调用，workVerificationId:', workVerificationId);
  currentWorkVerificationId.value = workVerificationId;
  showWorkVerifyDialog.value = true;
  verifyStatus.value = null;
  console.log('弹窗状态已设置为 true');
  // 自动开启摄像头
  nextTick(() => {
    console.log('准备开启摄像头');
    startCamera();
  });
};

const closeWorkVerifyDialog = () => {
  showWorkVerifyDialog.value = false;
  stopCamera();
  verifyStatus.value = null;
  currentWorkVerificationId.value = '';
};

const toggleCamera = async () => {
  if (isCameraActive.value) {
    stopCamera();
  } else {
    await startCamera();
  }
};

const startCamera = async () => {
  try {
    // #ifdef H5
    if (videoWrap.value) {
      const video = document.createElement('video');
      video.autoplay = true;
      video.playsInline = true;
      video.style.width = '100%';
      video.style.height = '200px';
      video.style.borderRadius = '8px';
      video.style.objectFit = 'cover';
      videoWrap.value.appendChild(video);
      videoEl.value = video;
      
      const mediaStream = await navigator.mediaDevices.getUserMedia({ 
        video: { 
          facingMode: 'user', 
          width: { ideal: 640 }, 
          height: { ideal: 480 } 
        } 
      });
      video.srcObject = mediaStream;
      stream.value = mediaStream;
    }
    // #endif
    isCameraActive.value = true;
    verifyStatus.value = { type: 'success', icon: '✅', message: '摄像头已开启' };
  } catch (e) {
    console.error('开启摄像头失败:', e);
    verifyStatus.value = { type: 'error', icon: '❌', message: '摄像头不可用' };
    isCameraActive.value = false;
  }
};

const stopCamera = () => {
  // #ifdef H5
  if (stream.value) {
    stream.value.getTracks().forEach(track => track.stop());
    stream.value = null;
  }
  if (videoEl.value) {
    videoEl.value.remove();
    videoEl.value = null;
  }
  // #endif
  isCameraActive.value = false;
  verifyStatus.value = null;
};

const onCameraError = () => {
  verifyStatus.value = { type: 'error', icon: '❌', message: '摄像头错误' };
  isCameraActive.value = false;
};

const captureBase64 = async () => {
  // #ifdef H5
  if (videoEl.value && videoEl.value.videoWidth && videoEl.value.videoHeight) {
    const canvas = document.createElement('canvas');
    canvas.width = videoEl.value.videoWidth;
    canvas.height = videoEl.value.videoHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(videoEl.value, 0, 0, canvas.width, canvas.height);
    return canvas.toDataURL('image/jpeg', 0.85);
  }
  // #endif
  // #ifdef APP-PLUS || MP-WEIXIN
  const cameraContext = uni.createCameraContext();
  const p = await new Promise((resolve, reject) => {
    cameraContext.takePhoto({
      quality: 'high',
      success: (res) => {
        uni.getFileSystemManager().readFile({
          filePath: res.tempImagePath,
          encoding: 'base64',
          success: (f) => resolve('data:image/jpeg;base64,' + f.data),
          fail: reject
        });
      },
      fail: reject
    });
  });
  return p;
  // #endif
};

const startVerification = async () => {
  if (!currentWorkVerificationId.value) {
    verifyStatus.value = { type: 'error', icon: '❌', message: '缺少核验参数' };
    return;
  }
  
  isVerifying.value = true;
  verifyStatus.value = { type: 'info', icon: '⏳', message: '正在核验...' };
  
  try {
    const frame = await captureBase64();
    if (!frame) {
      throw new Error('无法采集图像');
    }
    
    const token = uni.getStorageSync('accessToken');
    const res = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/work/verify`,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` },
      data: { 
        id: Number(currentWorkVerificationId.value), 
        frames: [frame], 
        action: 'shake_head' 
      }
    });
    
    if (res.statusCode === 200) {
      console.log('核验响应数据:', res.data);
      const verificationResult = res.data?.data?.result;
      console.log('核验结果:', verificationResult, '类型:', typeof verificationResult);
      console.log('当前 workVerificationId:', currentWorkVerificationId.value);
      
      if (verificationResult === 1) {
        verifyStatus.value = { type: 'success', icon: '✅', message: '核验成功' };
        actionMsg.value = '工作核验成功';
        // 立即刷新消息列表，确保获取最新数据
        fetchMessages();
        setTimeout(() => {
          closeWorkVerifyDialog();
        }, 1500);
      } else if (verificationResult === 0) {
        verifyStatus.value = { type: 'error', icon: '❌', message: '核验失败，请重新核验' };
        actionMsg.value = '工作核验失败';
        // 核验失败也要刷新消息列表，更新状态显示
        fetchMessages();
      } else {
        console.log('核验结果异常，值:', verificationResult);
        verifyStatus.value = { type: 'error', icon: '❌', message: `核验结果异常: ${verificationResult}` };
      }
    } else {
      console.log('核验请求失败，状态码:', res.statusCode, '响应:', res.data);
      verifyStatus.value = { type: 'error', icon: '❌', message: res.data?.message || '核验失败' };
    }
  } catch (e) {
    console.error('核验失败:', e);
    verifyStatus.value = { type: 'error', icon: '❌', message: e.message || '核验失败' };
  } finally {
    isVerifying.value = false;
  }
};
// 识别"队长转让/成为新队长/队长已变更"系统通知
const isLeaderTransferMsg = (msg) => {
  const title = (msg?.title || '').trim();
  return title.includes('队长转让') || title.includes('成为新队长') || title.includes('队长已变更');
};

const leaderTransferTypeLabel = (msg) => {
  const title = (msg?.title || '').trim();
  if (title.includes('成为新队长')) return '成为新队长';
  if (title.includes('队长已变更')) return '队长已变更';
  return '队长转让';
};

const typeText = (t, msg) => {
  const builtIn = MessageUtils.getTypeDescription(t);
  if (builtIn && builtIn !== '未知类型') return builtIn;
  if (isLeaderTransferMsg(msg)) return leaderTransferTypeLabel(msg);
  return '系统通知';
};

const statusText = (s, messageType, msg) => {
  // 系统类消息统一使用"未读/已读"
  const isSystem = MessageUtils?.isSystemMessage?.(messageType) || category.value === 'system' || isLeaderTransferMsg(msg);
  if (isSystem) {
    if (String(s) === '0') return '未读';
    if (String(s) === '1') return '已读';
    return '已读';
  }
  // 其余类型使用内置映射
  const builtIn = getStatusDescriptionByType(s, messageType);
  return builtIn || '未知状态';
};

// 统一解析消息的展示类型：如果文本里包含"投票"，一律按投票处理
const resolveMessageType = (msg) => {
  try {
    const title = msg?.title || '';
    const content = msg?.content || '';
    const extra = typeof msg?.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg?.extraData || {});
    const hasVoteSignal = title.includes('投票') || content.includes('投票') || !!extra.voteId || extra.type === 'vote' || extra.eventType === 'TEAM_VOTE';
    if (hasVoteSignal) return MessageType.TEAM_VOTE;
  } catch (_) {}
  return msg?.type;
};
const formatTime = (t) => {
  if (!t) return '-';
  return t.replace('T', ' ').slice(0, 19);
};

// 获取消息的过期时间
const getExpiresAt = (msg) => {
  try {
    // 优先检查投票消息的过期时间字段
    if (msg.expiresAt) {
      return msg.expiresAt;
    }
    if (msg.voteDeadline) {
      return msg.voteDeadline;
    }
    // 工作核验消息的过期时间
    const workVerification = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    return workVerification.verificationTime; // 使用 verificationTime 字段作为过期时间
  } catch (e) {
    return null;
  }
};

// 检查是否已过期
const isExpired = (msg) => {
  const expiresAt = getExpiresAt(msg);
  if (!expiresAt) return false;
  
  try {
    const raw = expiresAt;
    let expiresMs = null;
    if (typeof raw === 'number') {
      expiresMs = raw < 1e12 ? raw * 1000 : raw;
    } else if (typeof raw === 'string') {
      const trimmed = raw.trim();
      if (/^\d+$/.test(trimmed)) {
        const num = Number(trimmed);
        expiresMs = num < 1e12 ? num * 1000 : num;
      } else {
        // 处理字符串格式的时间
        const looksISO = trimmed.includes('T');
        const hasZone = /[zZ]|[+\-]\d{2}:?\d{2}$/.test(trimmed);
        
        if (looksISO && !hasZone) {
          // ISO格式但没有时区：添加UTC时区标识
          const toParse = trimmed + 'Z';
        const parsed = Date.parse(toParse);
        if (!isNaN(parsed)) expiresMs = parsed;
        } else if (!looksISO && !hasZone) {
          // 格式为 "yyyy-MM-dd HH:mm:ss"：后端LocalDateTime格式
          // 假设服务器在UTC+8时区，添加时区标识
          const dateStr = trimmed.replace(' ', 'T') + '+08:00';
          const parsed = Date.parse(dateStr);
          if (!isNaN(parsed)) {
            expiresMs = parsed;
          } else {
            // 备用方案：按本地时间解析
            const localParsed = Date.parse(trimmed.replace(' ', 'T'));
            if (!isNaN(localParsed)) expiresMs = localParsed;
          }
        } else {
          // 有时区信息，直接解析
          const parsed = Date.parse(trimmed);
          if (!isNaN(parsed)) expiresMs = parsed;
        }
      }
    } else if (raw instanceof Date) {
      expiresMs = raw.getTime();
    }
    
    if (expiresMs) {
      // 允许 90 秒时钟偏差的宽限
      return Date.now() - expiresMs > 90 * 1000;
    }
    return false;
  } catch (e) {
    return false;
  }
};

// 格式化倒计时显示（秒数转换为 MM:SS 或 HH:MM:SS 格式）
const formatCountdown = (seconds) => {
  if (!seconds || seconds <= 0) return '00:00';
  
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = seconds % 60;
  
  if (hours > 0) {
    return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  } else {
    return `${minutes}:${secs.toString().padStart(2, '0')}`;
  }
};

// 格式化过期时间显示
const formatExpiresTime = (expiresAt) => {
  if (!expiresAt) return '-';
  
  try {
    const raw = expiresAt;
    let expiresMs = null;
    if (typeof raw === 'number') {
      expiresMs = raw < 1e12 ? raw * 1000 : raw;
    } else if (typeof raw === 'string') {
      const trimmed = raw.trim();
      if (/^\d+$/.test(trimmed)) {
        const num = Number(trimmed);
        expiresMs = num < 1e12 ? num * 1000 : num;
      } else {
        // 处理字符串格式的时间
        const looksISO = trimmed.includes('T');
        const hasZone = /[zZ]|[+\-]\d{2}:?\d{2}$/.test(trimmed);
        
        if (looksISO && !hasZone) {
          // ISO格式但没有时区：添加UTC时区标识
          const toParse = trimmed + 'Z';
        const parsed = Date.parse(toParse);
        if (!isNaN(parsed)) expiresMs = parsed;
        } else if (!looksISO && !hasZone) {
          // 格式为 "yyyy-MM-dd HH:mm:ss"：后端LocalDateTime格式
          // 假设服务器在UTC+8时区，添加时区标识
          const dateStr = trimmed.replace(' ', 'T') + '+08:00';
          const parsed = Date.parse(dateStr);
          if (!isNaN(parsed)) {
            expiresMs = parsed;
          } else {
            // 备用方案：按本地时间解析
            const localParsed = Date.parse(trimmed.replace(' ', 'T'));
            if (!isNaN(localParsed)) expiresMs = localParsed;
          }
        } else {
          // 有时区信息，直接解析
          const parsed = Date.parse(trimmed);
          if (!isNaN(parsed)) expiresMs = parsed;
        }
      }
    } else if (raw instanceof Date) {
      expiresMs = raw.getTime();
    }
    
    if (expiresMs) {
      const now = Date.now();
      const diff = expiresMs - now;
      
      if (diff <= 0) {
        return '已过期';
      } else if (diff < 60 * 1000) {
        return `${Math.ceil(diff / 1000)}秒后过期`;
      } else if (diff < 60 * 60 * 1000) {
        return `${Math.ceil(diff / (60 * 1000))}分钟后过期`;
      } else {
        const date = new Date(expiresMs);
        return date.toLocaleString('zh-CN', {
          month: '2-digit',
          day: '2-digit',
          hour: '2-digit',
          minute: '2-digit',
          second: '2-digit'
        });
      }
    }
    return '-';
  } catch (e) {
    return '-';
  }
};

const updateUnderline = () => {
  nextTick(() => {
    const idx = typeTabs.findIndex(tab => tab.value === type.value);
    const tabEl = tabRefs.value[idx];
    if (tabEl) {
      underlineStyle.value = {
        width: tabEl.offsetWidth + 'px',
        left: tabEl.offsetLeft + 'px',
        transition: 'all 0.3s cubic-bezier(.4,0,.2,1)'
      };
    }
  });
};
onMounted(updateUnderline);
watch(type, updateUnderline);

// 消息点击或跳转处理函数
const handleMessageClickOrJump = async (msg, event) => {
  console.log('handleMessageClickOrJump 被调用，消息状态:', msg.status, 'taskId:', msg.taskId, 'type:', msg.type);
  
  // 检查是否是系统消息（兼容后端的系统通知类型：0, 101, 102, 103等）
  const backendSystemTypes = [0, 101, 102, 103]; // 后端的系统通知类型常量
  const isSystemMsg = MessageUtils?.isSystemMessage?.(msg.type) 
    || category.value === 'system' 
    || (msg.notificationType !== undefined && backendSystemTypes.includes(msg.notificationType))
    || (msg.type !== undefined && backendSystemTypes.includes(msg.type))
    || isLeaderTransferMsg(msg);
  
  console.log('是否为系统消息:', isSystemMsg, '当前分类:', category.value, 'notificationType:', msg.notificationType);
  
  // 如果是系统消息，特殊处理
  if (isSystemMsg) {
    // 如果未读，先标记为已读
    if (msg.status === 0 || msg.status === SystemNotificationStatus.UNREAD) {
      console.log('系统消息未读，先标记为已读');
      await markAsRead(msg.id, 'system');
      msg.status = SystemNotificationStatus.READ || 1; // 更新本地状态
    }
    
    // 如果有taskId，跳转到任务详情
    if (msg.taskId) {
      console.log('系统消息包含任务ID，跳转到任务详情页面，taskId:', msg.taskId);
      uni.navigateTo({
        url: `/pages/task/detail/index?id=${msg.taskId}`
      });
      return;
    }
    
    // 如果没有taskId，显示详情弹窗
    console.log('系统消息无任务ID，显示详情');
    uni.showModal({
      title: msg.title || '系统通知',
      content: msg.content || '',
      showCancel: false,
      confirmText: '确定'
    });
    return;
  }
  
  // 非系统消息：如果是待处理状态（status === 0），执行原有的点击逻辑
  if (msg.status === 0) {
    console.log('消息待处理，执行原有的点击逻辑');
    handleMessageClick(msg, event);
    return;
  }
  
  // 非系统消息且已处理：如果消息有taskId，跳转到任务详情
  if (msg.taskId) {
    console.log('消息包含任务ID，跳转到任务详情页面，taskId:', msg.taskId);
    uni.navigateTo({
      url: `/pages/task/detail/index?id=${msg.taskId}`
    });
    return;
  }
  
  // 其他情况不做任何处理
  console.log('消息已处理且无任务ID，不做任何操作');
};

const handleMessageClick = async (msg, event) => {
  // 阻止默认行为和事件冒泡
  if (event) {
    event.preventDefault();
    event.stopPropagation();
  }
  
  console.log('handleMessageClick 被调用，消息类型:', msg.type);
  console.log('消息对象:', msg);
  console.log('MessageType.TASK_COMPLETION 值:', MessageType.TASK_COMPLETION);
  console.log('UserApplicationStatus.APPROVED 值:', UserApplicationStatus.APPROVED);
  console.log('UserApplicationStatus.REJECTED 值:', UserApplicationStatus.REJECTED);
  console.log('MessageType.SYSTEM_NOTIFICATION_TEAM_VOTE 值:', MessageType.SYSTEM_NOTIFICATION_TEAM_VOTE);
  console.log('MessageType.SYSTEM_NOTIFICATION_TASK_CANCELLED 值:', MessageType.SYSTEM_NOTIFICATION_TASK_CANCELLED);
  console.log('消息标题包含投票:', msg.title?.includes('投票'));
  console.log('消息内容包含投票:', msg.content?.includes('投票'));
  
  // 处理不同类型的消息点击
  try {
    console.log('开始处理消息点击，消息类型:', msg.type);
    const workVerification = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    // 提前兜底：凡是标题/内容/extraData包含投票信号的，优先按投票处理
    try {
      const title = msg?.title || '';
      const content = msg?.content || '';
      const hasVoteSignal = title.includes('投票') || content.includes('投票') || !!workVerification?.voteId || workVerification?.type === 'vote' || workVerification?.eventType === 'TEAM_VOTE';
      if (hasVoteSignal) {
        if (msg.taskId) {
          showVoteModal(msg);
        } else {
          uni.showToast({ title: '请前往任务页面参与投票', icon: 'none' });
        }
        return;
      }
    } catch (_) {}
    
    // AI远程提问消息
    if (msg.type === MessageType.AI_EXAM) {
      console.log('点击了AI远程提问消息，准备处理');
      
      const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
      // 兼容后端返回格式：使用id作为examTaskId
      const examTaskId = extraData.examTaskId || extraData.id;
      const status = extraData.status;
      
      console.log('提取的 examTaskId:', examTaskId, 'status:', status);
      
      if (!examTaskId) {
        console.log('缺少 examTaskId');
        uni.showToast({ title: '缺少考试参数', icon: 'none' });
        return;
      }
      
      // 兼容数字类型的status值
      if (status === 'pending' || status === 0) {
        // 待预约状态，打开预约弹窗
        const questionCount = extraData.questionCount || 4; // 默认4题
        openExamScheduleDialog(examTaskId, questionCount, msg.id);
      } else if (status === 'scheduled' || status === 1) {
        // 已预约状态，检查是否到时间
        const scheduledTime = extraData.scheduledStartTime;
        if (scheduledTime) {
          const now = new Date();
          const scheduled = new Date(scheduledTime);
          if (now < scheduled) {
            uni.showToast({ 
              title: `预约时间还未到！请在${formatScheduledTime(scheduledTime)}再开始考核`, 
              icon: 'none',
              duration: 3000
            });
            return;
          }
        }
        // 到时间了，跳转到考试页面
        uni.navigateTo({
          url: `/pages/exam/index?examTaskId=${examTaskId}`
        });
      } else if (status === 'in_progress' || status === 2) {
        // 进行中，跳转到考试页面
        uni.navigateTo({
          url: `/pages/exam/index?examTaskId=${examTaskId}`
        });
      } else if (status === 'completed' || status === 3 || status === 'abnormal' || status === 4) {
        // 已完成或异常，跳转到考试结果页面
        uni.navigateTo({
          url: `/pages/exam/result?examTaskId=${examTaskId}`
        });
      } else {
        uni.showToast({ title: '考试状态异常', icon: 'none' });
      }
    }
    // 企业入驻申请消息
    else if (workVerification && workVerification.action === 'membership_application') {
      currentAction.value = { id: msg.id, type: 'org-apply', approve: true };
      dialogTitle.value = '处理企业入驻申请';
      dialogPlaceholder.value = '若选择拒绝，可填写拒绝理由（可选）';
      showReasonDialog.value = true;
    }
    // 任务申请消息
    else if (msg.type === MessageType.TASK_APPLICATION && msg.status !== UserApplicationStatus.APPROVED && msg.status !== UserApplicationStatus.REJECTED) {
      // 解析团队信息
      let teamInfo = null;
      try {
        const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
        if (extraData.isTeam) {
          teamInfo = extraData;
          // 预加载团队成员信息
          if (teamInfo.members && teamInfo.members.length > 0) {
            teamInfo.members.forEach(member => {
              getUserInfo(member.userId);
            });
          }
          // 预加载队长信息
          if (teamInfo.leaderId) {
            getUserInfo(teamInfo.leaderId);
          }
        }
      } catch (e) {
        console.error('解析团队信息失败:', e);
      }
      
      currentAction.value = { id: msg.id, type: 'task-apply', approve: true, teamInfo: teamInfo };
      dialogTitle.value = teamInfo ? '处理团队任务申请' : '处理任务申请';
      dialogPlaceholder.value = '若选择拒绝，可填写拒绝理由（可选）';
      showReasonDialog.value = true;
    }
    // 任务申请撤回消息 - 只显示状态，无需操作
    else if (msg.type === MessageType.TASK_WITHDRAWAL) {
      uni.showToast({ title: '任务申请已撤回', icon: 'none' });
      return;
    }
    // 任务完成申请撤回消息 - 只显示状态，无需操作
    else if (msg.type === MessageType.TASK_COMPLETION_WITHDRAWAL) {
      uni.showToast({ title: '任务完成申请已撤回', icon: 'none' });
      return;
    }
    // 任务完成申请消息 - 直接跳转到任务界面
    else if (msg.type === MessageType.TASK_COMPLETION || msg.type === 4) {
      console.log('=== 任务完成申请消息处理 ===');
      console.log('消息对象:', JSON.stringify(msg, null, 2));
      
      // 提取任务ID
      let taskId = null;
      try {
        const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
        taskId = extraData.taskId || extraData.task_id || extraData.id || msg.taskId;
        console.log('提取的任务ID:', taskId);
      } catch (e) {
        console.error('解析任务ID失败:', e);
      }
      
      if (taskId) {
        console.log('跳转到任务界面，任务ID:', taskId);
        // 直接跳转到任务界面，并传递消息ID用于显示申请完成详情
        uni.navigateTo({
          url: `/pages/task/detail/index?id=${taskId}&messageId=${msg.id}&showCompletion=true`
        });
      } else {
        console.error('未找到任务ID');
        uni.showToast({ title: '未找到任务信息', icon: 'none' });
      }
    }
    // 任务取消申请消息
    else if (msg.type === MessageType.TASK_CANCELLATION) {
      currentAction.value = { id: msg.id, type: 'task-cancel-request', approve: true };
      dialogTitle.value = '处理任务取消申请';
      dialogPlaceholder.value = '若选择拒绝，可填写拒绝理由（可选）';
      showReasonDialog.value = true;
    }
    // 任务取消申请处理结果消息 - 只显示状态，无需操作
    else if (msg.type === MessageType.TASK_CANCELLATION_RESULT) {
      const isApproved = msg.title.includes('已同意');
      uni.showToast({ 
        title: isApproved ? '取消申请已同意' : '取消申请被拒绝', 
        icon: isApproved ? 'success' : 'none' 
      });
      return;
    }
    // 工作核验消息
    else if (msg.type === MessageType.WORK_VERIFICATION) {
      console.log('点击了工作核验消息，准备检查状态');
      
      // 使用统一的过期检查函数
      if (isExpired(msg)) {
        console.log('核验已过期');
        uni.showToast({ title: '核验已过期', icon: 'none' });
        return;
      }
      
      const workVerificationId = workVerification && workVerification.id;
      console.log('提取的 workVerificationId:', workVerificationId);
      
      if (!workVerificationId) {
        console.log('缺少 workVerificationId');
        uni.showToast({ title: '缺少核验参数', icon: 'none' });
        return;
      }
      
      // 检查核验状态（从消息extraData中获取）
      const verificationResult = getVerificationStatus(msg);
      console.log('核验状态:', verificationResult);
      
      if (verificationResult === 1) {
        // 核验成功
        uni.showToast({ title: '已核验成功', icon: 'success' });
      } else if (verificationResult === 0) {
        // 核验失败，允许重新核验
        uni.showToast({ title: '核验失败，请重新核验', icon: 'none' });
        openWorkVerifyDialog(workVerificationId);
      } else {
        // 未核验过或其他状态，提示未核验并打开核验弹窗
        console.log('准备打开工作核验弹窗，当前状态:', verificationResult);
        uni.showToast({ title: '未核验', icon: 'none' });
        openWorkVerifyDialog(workVerificationId);
      }
    }
    // 任务修改申请消息处理（优先检测）
    else if (msg.title?.includes('任务修改申请')) {
      console.log('检测到任务修改申请消息:', {
        type: msg.type,
        title: msg.title,
        content: msg.content,
        taskId: msg.taskId
      });
      openTaskModificationModal(msg);
    }
    // 投票相关消息处理
    else if (msg.title?.includes('投票') || msg.content?.includes('投票')) {
      console.log('检测到投票相关消息（通过标题或内容）:', {
        type: msg.type,
        title: msg.title,
        content: msg.content,
        taskId: msg.taskId,
        MessageType: MessageType
      });
      
      if (msg.taskId) {
        console.log('显示投票界面，任务ID:', msg.taskId);
        // 显示投票界面而不是跳转
        showVoteModal(msg);
      } else {
        console.log('通过内容检测到投票消息，但没有任务ID');
        uni.showToast({ title: '请前往任务页面参与投票', icon: 'none' });
      }
    }
    // 团队投票消息（特殊处理）
    else if (msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_VOTE || 
             (msg.type === MessageType.SYSTEM_NOTIFICATION_TASK_CANCELLED && 
              (msg.title?.includes('投票') || msg.content?.includes('投票')))) {
      console.log('点击了团队投票消息，类型:', msg.type);
      console.log('消息详情:', {
        type: msg.type,
        title: msg.title,
        content: msg.content,
        taskId: msg.taskId,
        MessageType: MessageType,
        isVoteMessage: msg.title?.includes('投票') || msg.content?.includes('投票')
      });
      
      // 显示投票界面而不是跳转
      if (msg.taskId) {
        console.log('显示投票界面，任务ID:', msg.taskId);
        showVoteModal(msg);
      } else {
        console.log('没有任务ID，显示提示');
        uni.showToast({ title: '请前往任务页面参与投票', icon: 'none' });
      }
    }
    // 团队相关系统消息
    else if (msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_INVITATION_ACCEPTED ||
             msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_INVITATION_REJECTED ||
             msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_INVITATION_WITHDRAWN ||
             msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_TASK_APPLIED ||
             msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_TASK_STARTED ||
             msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_TASK_CANCELLED ||
             msg.type === MessageType.SYSTEM_NOTIFICATION_TEAM_TASK_NO_NEED) {
      console.log('点击了团队系统消息，类型:', msg.type);
      
      // 团队系统消息通常包含任务ID，跳转到任务详情页
      if (msg.taskId) {
        uni.navigateTo({
          url: `/pages/task/detail/index?id=${msg.taskId}`
        });
      } else {
        // 如果没有任务ID，只显示消息内容
        uni.showToast({ title: msg.title || '团队消息', icon: 'none' });
      }
    }
    // 系统通知消息 - 包括所有系统通知类型（-128 到 -1）
    else if (MessageUtils.isSystemMessage(msg.type)) {
      console.log('点击了系统通知消息，类型:', msg.type);
      
      // 如果是未读状态，自动标记为已读
      if (msg.status === 0 || msg.status === SystemNotificationStatus.UNREAD) {
        await markAsRead(msg.id, 'system');
        msg.status = SystemNotificationStatus.READ;
      }
      
      // 如果包含任务ID，跳转到任务详情页
      if (msg.taskId) {
        uni.navigateTo({
          url: `/pages/task/detail/index?id=${msg.taskId}`
        });
      } else {
        // 显示消息详情
        uni.showModal({
          title: msg.title || '系统通知',
          content: msg.content || '',
          showCancel: false,
          confirmText: '确定'
        });
      }
    }
    // 团队邀请消息
    else if (msg.type === MessageType.TEAM_INVITATION) {
      console.log('点击了团队邀请消息');
      handleTeamInvitationClick(msg);
    }
    // 好友请求消息
    else if (msg.type === MessageType.FRIEND_REQUEST) {
      console.log('点击了好友请求消息');
      handleFriendRequestClick(msg);
    }
  } catch (e) {
    // 忽略解析错误
  }
};

// 处理团队邀请消息点击
const handleTeamInvitationClick = async (msg) => {
  console.log('处理团队邀请消息:', msg);
  
  try {
    const extraData = typeof msg.extraData === 'string' ? JSON.parse(msg.extraData || '{}') : (msg.extraData || {});
    // 优先从消息对象的teamId字段读取，如果没有则从extraData读取
    const teamId = msg.teamId || extraData.teamId;
    const taskId = msg.taskId || extraData.taskId;
    const leaderId = msg.senderId || extraData.leaderId;
    const teamName = extraData.teamName;
    
    console.log('团队邀请信息:', { teamId, taskId, leaderId, teamName, msgTeamId: msg.teamId, extraDataTeamId: extraData.teamId });
    
    if (!teamId) {
      uni.showToast({ title: '缺少团队信息', icon: 'none' });
      return;
    }
    
    // 显示团队邀请处理对话框
    showTeamInvitationModal.value = true;
    currentTeamInvitation.value = {
      teamId,
      taskId,
      leaderId,
      teamName,
      leaderNickname: '加载中...',
      leaderAvatar: '/static/images/default-avatar.png',
      taskTitle: extraData.taskTitle,
      taskContent: extraData.taskContent
    };
    
    // 根据sender的uid获取真实的用户信息
    if (leaderId) {
      await fetchUserInfoForInvitation(leaderId);
    }
    
    // 根据taskId获取任务详细信息
    if (taskId) {
      await fetchTaskInfoForInvitation(taskId);
    }
    
  } catch (error) {
    console.error('处理团队邀请消息失败:', error);
    uni.showToast({ title: '处理团队邀请失败', icon: 'none' });
  }
};

// 处理团队邀请（同意/拒绝）
const handleTeamInvitation = async (teamId, accepted) => {
  try {
    const token = uni.getStorageSync('accessToken');
    
    if (!token) {
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }
    
    const response = await uni.request({
      url: `${NETWORK_CONFIG.API_BASE_URL}/message/handle-team-invitation`,
      method: 'POST',
      header: { 
        'Access-Token': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      data: {
        teamId: teamId,
        accepted: accepted
      },
      timeout: 10000
    });
    
    if (response.statusCode === 200 && response.data) {
      // 解析响应数据（可能是字符串或对象）
      let responseData = response.data;
      if (typeof responseData === 'string') {
        try {
          responseData = JSON.parse(responseData);
        } catch (e) {
          console.error('解析响应数据失败:', e);
          responseData = { success: false, message: '响应数据格式错误' };
        }
      }
      
      // 检查是否有 success 字段
      if (responseData.success !== false) {
        const action = accepted ? '同意' : '拒绝';
        uni.showToast({ title: `已${action}团队邀请`, icon: 'success' });
        // 刷新消息列表
        await fetchMessages();
      } else {
        // 处理失败情况
        const errorCode = responseData.errorCode;
        const errorMessage = responseData.message || '处理失败';
        
        // 根据错误码显示不同的提示
        if (errorCode === 'IS_LEADER') {
          // 用户是其他团队的队长
          uni.showModal({
            title: '无法加入团队',
            content: errorMessage,
            showCancel: false,
            confirmText: '知道了'
          });
        } else if (errorCode === 'IS_MEMBER') {
          // 用户是其他团队的队员
          uni.showModal({
            title: '无法加入团队',
            content: errorMessage,
            showCancel: false,
            confirmText: '知道了'
          });
        } else {
          // 其他错误
          uni.showToast({ title: errorMessage, icon: 'none', duration: 3000 });
        }
        
        // 注意：不刷新消息列表，让邀请消息保持待处理状态
        // 用户处理完现有团队后，可以再次点击同意
      }
    } else {
      // 非200状态码或其他错误
      const errorMessage = response.data?.message || '处理失败';
      uni.showToast({ title: errorMessage, icon: 'none' });
    }
  } catch (error) {
    console.error('处理团队邀请失败:', error);
    uni.showToast({ title: '网络错误或处理失败', icon: 'none' });
  }
};

// 处理好友请求消息点击
const handleFriendRequestClick = (msg) => {
  console.log('处理好友请求消息:', msg);
  
  // 从消息内容中提取发送者信息
  const content = msg.content || '';
  const match = content.match(/用户\s+(\S+)\s+想要添加您为好友/);
  
  if (match) {
    const senderName = match[1];
    uni.showModal({
      title: '好友请求',
      content: `${senderName} 想要添加您为好友，是否同意？`,
      confirmText: '同意',
      cancelText: '拒绝',
      success: (res) => {
        if (res.confirm) {
          // 同意好友请求
          acceptFriendRequest(msg, senderName);
        } else if (res.cancel) {
          // 拒绝好友请求
          rejectFriendRequest(msg, senderName);
        }
      }
    });
  } else {
    uni.showToast({ title: '无法解析好友请求信息', icon: 'none' });
  }
};

// 同意好友请求
const acceptFriendRequest = async (msg, senderName) => {
  try {
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }

    // 优先使用消息中的senderId
    let senderId = msg.senderId;
    
    // 如果senderId为空，通过昵称查找用户ID
    if (!senderId && senderName) {
      try {
        const searchResponse = await uni.request({
          url: `${NETWORK_CONFIG.API_BASE_URL}/friends/search?keyword=${encodeURIComponent(senderName)}`,
          method: 'GET',
          header: { 'Access-Token': `Bearer ${token}` }
        });
        
        if (searchResponse.data && searchResponse.data.code === 200 && searchResponse.data.data.length > 0) {
          // 找到匹配的用户
          const matchedUser = searchResponse.data.data.find(user => user.friendNickname === senderName);
          if (matchedUser) {
            senderId = matchedUser.friendId;
          }
        }
      } catch (searchError) {
        console.error('搜索用户失败:', searchError);
      }
    }
    
    if (!senderId) {
      uni.showToast({ title: '无法获取发送者信息', icon: 'none' });
      return;
    }

    // 调用后端API同意好友请求
    const response = await uni.request({
          url: `${NETWORK_CONFIG.API_BASE_URL}/friends/accept`,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` },
      data: { friendId: senderId }
    });

    if (response.data && response.data.code === 200) {
      uni.showToast({ title: '已同意好友请求', icon: 'success' });
      // 刷新消息列表
      fetchMessages();
    } else {
      uni.showToast({ title: response.data?.message || '操作失败', icon: 'none' });
    }
    
  } catch (error) {
    console.error('同意好友请求失败:', error);
    uni.showToast({ title: '操作失败', icon: 'none' });
  }
};

// 拒绝好友请求
const rejectFriendRequest = async (msg, senderName) => {
  try {
    const token = uni.getStorageSync('accessToken');
    if (!token) {
      uni.showToast({ title: '请先登录', icon: 'none' });
      return;
    }

    // 优先使用消息中的senderId
    let senderId = msg.senderId;
    
    // 如果senderId为空，通过昵称查找用户ID
    if (!senderId && senderName) {
      try {
        const searchResponse = await uni.request({
          url: `${NETWORK_CONFIG.API_BASE_URL}/friends/search?keyword=${encodeURIComponent(senderName)}`,
          method: 'GET',
          header: { 'Access-Token': `Bearer ${token}` }
        });
        
        if (searchResponse.data && searchResponse.data.code === 200 && searchResponse.data.data.length > 0) {
          // 找到匹配的用户
          const matchedUser = searchResponse.data.data.find(user => user.friendNickname === senderName);
          if (matchedUser) {
            senderId = matchedUser.friendId;
          }
        }
      } catch (searchError) {
        console.error('搜索用户失败:', searchError);
      }
    }
    
    if (!senderId) {
      uni.showToast({ title: '无法获取发送者信息', icon: 'none' });
      return;
    }

    // 调用后端API拒绝好友请求
    const response = await uni.request({
          url: `${NETWORK_CONFIG.API_BASE_URL}/friends/reject`,
      method: 'POST',
      header: { 'Access-Token': `Bearer ${token}` },
      data: { friendId: senderId }
    });

    if (response.data && response.data.code === 200) {
      uni.showToast({ title: '已拒绝好友请求', icon: 'success' });
      // 刷新消息列表
      fetchMessages();
    } else {
      uni.showToast({ title: response.data?.message || '操作失败', icon: 'none' });
    }
    
  } catch (error) {
    console.error('拒绝好友请求失败:', error);
    uni.showToast({ title: '操作失败', icon: 'none' });
  }
};

// 初始化状态选项
currentStatusOptions.value = statusOptionsConfig['system'] || [];

// 进入页面默认拉取
fetchMessages();
fetchUnreadCounts(); // 获取未读消息计数

// 页面显示时刷新数据
onShow(() => {
  console.log('🔄 消息中心 onShow - 刷新数据');
  fetchUnreadCounts(); // 刷新未读消息计数
  
  // 监听任务相关更新事件
  uni.$on('task-related-update', (data) => {
    console.log('🔄 消息中心收到任务相关更新事件:', data);
    // 刷新消息列表
    fetchMessages();
  });
  
  // 每次进入页面都刷新消息列表
  fetchMessages();
});

// 打开对话框时，如为任务完成申请，尝试拉取申请详情
watch(showReasonDialog, (visible) => {
  if (visible && currentAction.value && currentAction.value.type === 'task-completion') {
    // 优先尝试从 extra 取 taskId；否则从消息字段中兜底（若后端返回）
    try {
      const targetMsg = (messages.value?.content || []).find(m => m.id === currentAction.value.id);
      const extra = targetMsg && (typeof targetMsg.extraData === 'string' ? JSON.parse(targetMsg.extraData || 'null') : (targetMsg.extraData));
      // 1) 若 extraData 本身就是徽章分配数组，直接展示
      if (Array.isArray(extra) && extra.length && extra[0].badgeId) {
        previewLoading.value = false;
        previewError.value = '';
        completePreview.value = { proof: '', badges: extra };
        return;
      }
      // 2) 否则按对象处理，提取 taskId 后请求后端
      const extraObj = extra || {};
      const taskId = extraObj.taskId || extraObj.task_id || extraObj.id || targetMsg?.taskId;
      if (taskId) {
        fetchTaskCompletionPreview(taskId);
      } else {
        previewError.value = '未找到任务ID或分配数据';
      }
    } catch (e) {
      previewError.value = '解析任务ID失败';
    }
  } else {
    // 关闭或非该类型，清理预览状态
    previewLoading.value = false;
    previewError.value = '';
    completePreview.value = null;
  }
});
</script>

<style scoped>
.message-center {
  max-width: 100vw;
  margin: 0 auto;
  min-height: 100vh;
  padding: 12px 8px 20px 8px;
  background: #f5f5f5;
  position: relative;
  z-index: 1;
}

/* 横向选项卡样式 */
.horizontal-tabs {
  display: flex;
  background: #fff;
  border-bottom: 1px solid #f0f0f0;
  position: sticky;
  top: 0;
  z-index: 10;
  margin-bottom: 12px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.main-tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px 12px;
  cursor: pointer;
  transition: all 0.3s ease;
  border-bottom: 3px solid transparent;
  position: relative;
}

.main-tab:hover {
  background: #f8f9fa;
}

.main-tab.active {
  color: #4A90E2;
  background: #f0f6ff;
  border-bottom-color: #4A90E2;
  font-weight: 600;
}

.tab-name {
  font-size: 14px;
  font-weight: 500;
}

.tab-count {
  margin-left: 6px;
  background: #4A90E2;
  color: white;
  padding: 2px 6px;
  border-radius: 10px;
  font-size: 12px;
  min-width: 18px;
  text-align: center;
}

.unread-dot {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 8px;
  height: 8px;
  background: #ff4757;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px #ff4757;
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.2);
    opacity: 0.8;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* 子选项卡样式 */
.sub-tabs {
  display: flex;
  background: #f8f9fa;
  border-bottom: 1px solid #e9ecef;
  padding: 8px 16px;
  overflow-x: auto;
  white-space: nowrap;
}

.sub-tab {
  padding: 8px 16px;
  margin-right: 8px;
  background: #fff;
  border: 1px solid #e9ecef;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-size: 13px;
  color: #666;
  white-space: nowrap;
}

.sub-tab:hover {
  background: #e9ecef;
  color: #333;
}

.sub-tab.active {
  background: #4A90E2;
  color: white;
  border-color: #4A90E2;
  font-weight: 500;
}

/* 移除旧的折叠式选项卡样式，使用新的横向选项卡样式 */

.tab-underline {
  position: absolute;
  bottom: 4px;
  height: 2px;
  border-radius: 1px;
  background: #4A90E2;
  transition: all 0.3s cubic-bezier(.4,0,.2,1);
  z-index: 0;
}

/* 筛选区域 */
.filters {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
  align-items: center;
  flex-wrap: wrap;
  background: white;
  padding: 12px;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.filter-group {
  flex: 1;
  min-width: 120px;
}

.filter-inline {
  display: flex;
  align-items: center;
  gap: 8px; /* 控制label和select间距 */
}
.filter-inline .filter-label {
  margin-bottom: 0; /* 去掉label下边距 */
}

.filter-label {
  font-size: 13px;
  font-weight: 500;
  color: #666;
  margin-right: 8px;
}

.kawaii-select {
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid #e0e0e0;
  background-color: white;
  min-width: 100px;
  font-size: 13px;
  color: #333;
  transition: all 0.3s ease;
  cursor: pointer;
  appearance: none;
  background-image: url('data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6L8 10L12 6" stroke="%23666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 16px 16px;
  padding-right: 32px;
}

.kawaii-select:focus {
  border-color: #4A90E2;
  outline: none;
}

.kawaii-btn {
  padding: 8px 16px;
  border-radius: 8px;
  border: none;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 13px;
}

.reset-btn {
  background: #f0f0f0;
  color: #666;
  box-shadow: none;
  border: none;
}

.reset-btn:hover {
  background: #e0e0e0;
  color: #333;
}

.mark-all-read-btn {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
  border: none;
}

.mark-all-read-btn:hover {
  background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
  box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
  transform: translateY(-1px);
}

/* 消息列表 */
.message-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.message-card {
  background: white;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  border: 1px solid #f0f0f0;
  padding: 16px;
  margin-bottom: 0;
  font-size: 14px;
  position: relative;
  transition: background 0.5s cubic-bezier(.4,0,.2,1), border 0.5s cubic-bezier(.4,0,.2,1), box-shadow 0.5s, transform 0.5s, opacity 0.5s;
  display: flex;
  flex-direction: column;
  gap: 8px;
  cursor: pointer;
  opacity: 1;
  transform: scale(1);
}

.message-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  transform: translateY(-1px);
}

.message-card.unread {
  background: #fff8e1;
  border: 1px solid #ffcc02;
  box-shadow: 0 4px 16px rgba(255,204,2,0.15);
  transform: scale(1.01);
  opacity: 1;
}

.message-card:not(.unread) {
  background: #fff;
  border: 1px solid #f0f0f0;
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
  transform: scale(1);
  opacity: 1;
}

.message-card.unread::before {
  content: '';
  position: absolute;
  left: 12px;
  top: 12px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ff3b30;
  z-index: 2;
}

/* 已同意消息 */
.message-card.approved {
  background: #f0fff4;
  border: 1px solid #68d391;
}

/* 已拒绝消息 */
.message-card.rejected {
  background: #fff5f5;
  border: 1px solid #fc8181;
}

/* 已撤回消息 */
.message-card.withdrawn {
  background: #f8f9fa;
  border: 1px solid #6c757d;
  opacity: 0.8;
}

/* 已失效消息 */
.message-card.invalid {
  background: #f8f9fa;
  border: 1px solid #9e9e9e;
  opacity: 0.7;
}

/* 已处理消息 - 可点击跳转 */
.message-card.processed {
  cursor: pointer;
  opacity: 0.7;
}

.message-card.processed:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  opacity: 0.9;
}

.msg-title {
  font-size: 15px;
  margin-bottom: 4px;
  line-height: 1.4;
  padding-left: 0;
  font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
  font-weight: 500;
  color: #333;
}

.msg-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 12px;
  color: #888;
  margin-top: 4px;
  margin-bottom: 0;
  flex-wrap: wrap;
}

.type-badge, .status-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 500;
  margin-right: 4px;
  background: #f0f0f0;
  color: #666;
  border: none;
}

.type-badge {
  background: #e3eafc;
  color: #4A90E2;
}

.status-badge {
  background: #f4f6fa;
  color: #888;
}

.msg-time {
  font-size: 11px;
  color: #bbb;
  margin-left: auto;
}

/* 投票倒计时徽章 */
.countdown-badge.vote-countdown {
  display: inline-flex;
  align-items: center;
  padding: 4px 8px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 600;
  background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
  color: white;
  margin-left: 8px;
  box-shadow: 0 2px 4px rgba(255, 107, 107, 0.3);
  animation: countdown-pulse 2s infinite;
}

.countdown-badge.vote-countdown::before {
  content: '⏱️';
  margin-right: 4px;
  font-size: 14px;
}

@keyframes countdown-pulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.9;
    transform: scale(1.05);
  }
}

/* 倒计时即将结束时（少于30秒）的特殊样式 */
.countdown-badge.vote-countdown.urgent {
  background: linear-gradient(135deg, #ff4757 0%, #c44569 100%);
  animation: countdown-urgent 1s infinite;
}

@keyframes countdown-urgent {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
    box-shadow: 0 2px 4px rgba(255, 71, 87, 0.5);
  }
  50% {
    opacity: 0.85;
    transform: scale(1.1);
    box-shadow: 0 4px 8px rgba(255, 71, 87, 0.7);
  }
}

/* 过期时间显示 */
.expires-info {
  margin-top: 6px;
  padding: 6px 8px;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 3px solid #4A90E2;
  font-size: 12px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.expires-label {
  color: #666;
  font-weight: 500;
}

.expires-time {
  color: #4A90E2;
  font-weight: 600;
}

.expires-time.expired {
  color: #e53e3e;
  background: #fff5f5;
  padding: 2px 6px;
  border-radius: 4px;
  font-weight: 600;
}

/* 工作核验信息容器 */
.work-verify-info {
  margin-top: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* AI远程提问信息容器 */
.ai-exam-info {
  margin-top: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* 预约时间显示 */
.scheduled-info {
  margin-top: 6px;
  padding: 6px 8px;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 3px solid #4A90E2;
  font-size: 12px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.scheduled-label {
  color: #666;
  font-weight: 500;
}

.scheduled-time {
  color: #4A90E2;
  font-weight: 600;
}

/* 考试状态显示 */
.exam-status {
  padding: 6px 8px;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 3px solid #6c757d;
  font-size: 12px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.status-scheduled {
  color: #17a2b8;
  background: #d1ecf1;
}

.status-in-progress {
  color: #ffc107;
  background: #fff3cd;
}

.status-completed {
  color: #28a745;
  background: #d4edda;
}

.status-abnormal {
  color: #dc3545;
  background: #f8d7da;
}

/* 核验状态显示 */
.verification-status {
  padding: 6px 8px;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 3px solid #6c757d;
  font-size: 12px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.status-label {
  color: #666;
  font-weight: 500;
}

.status-value {
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
}

.status-success {
  color: #28a745;
  background: #d4edda;
}

.status-failed {
  color: #dc3545;
  background: #f8d7da;
}

.status-pending {
  color: #ffc107;
  background: #fff3cd;
}

/* 分页 */
.pagination {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 16px 0;
  background: white;
  padding: 12px;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.page-info {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: 13px;
  color: #666;
}

.page-btn {
  background: #4A90E2;
  color: white;
  box-shadow: 0 2px 4px rgba(74, 144, 226, 0.3);
  min-width: 80px;
  font-size: 12px;
}

.page-btn:hover {
  background: #357abd;
}

.page-btn:disabled {
  background: #ccc;
  cursor: not-allowed;
  box-shadow: none;
}

.page-text {
  font-weight: 500;
}

.count-text {
  font-size: 11px;
  opacity: 0.8;
}

/* 加载状态 */
.kawaii-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 0;
  background: white;
  border-radius: 12px;
  margin: 12px 0;
}

.loading-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin: 0 2px;
  border-radius: 50%;
  background: #4A90E2;
  animation: loading 1.4s infinite ease-in-out both;
}

.loading-dot:nth-child(1) { animation-delay: -0.32s; }
.loading-dot:nth-child(2) { animation-delay: -0.16s; }
.loading-dot:nth-child(3) { animation-delay: 0s; }

@keyframes loading {
  0%, 80%, 100% { transform: scale(0); }
  40% { transform: scale(1); }
}

.loading-text {
  margin-top: 12px;
  color: #666;
  font-size: 13px;
}

/* 错误状态 */
.kawaii-error {
  padding: 20px;
  border-radius: 12px;
  background: #fff5f5;
  color: #e53e3e;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 12px 0;
}

.error-icon {
  font-size: 24px;
}

.error-text {
  font-size: 13px;
}

/* 操作消息 */
.action-msg {
  margin-top: 12px;
  color: #38a169;
  background: #f0fff4;
  padding: 8px 16px;
  border-radius: 8px;
  text-align: center;
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  animation: fadeInUp 0.5s ease;
  font-size: 13px;
}

.action-icon {
  font-size: 16px;
}

@keyframes fadeInUp {
  from { 
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 工作核验弹窗 */
.work-verify-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
  animation: fadeIn 0.3s ease;
}

.work-verify-dialog {
  background: white;
  border-radius: 16px;
  padding: 20px;
  width: 90vw;
  max-width: 500px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  position: relative;
  animation: popIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.verify-hint {
  color: #666;
  font-size: 14px;
  margin-bottom: 16px;
  text-align: center;
}

.camera-container {
  margin: 16px 0;
  border-radius: 12px;
  overflow: hidden;
  background: #f5f5f5;
  min-height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.video-container {
  width: 100%;
  height: 200px;
  border-radius: 12px;
  overflow: hidden;
  background: #000;
}

.camera-view {
  width: 100%;
  height: 200px;
  border-radius: 12px;
}

.camera-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 200px;
  color: #999;
}

.placeholder-icon {
  font-size: 48px;
  margin-bottom: 8px;
}

.placeholder-text {
  font-size: 14px;
}

.verify-controls {
  display: flex;
  gap: 12px;
  margin: 16px 0;
  justify-content: center;
}

.camera-btn {
  background: #4A90E2;
  color: white;
  min-width: 120px;
}

.verify-btn {
  background: #00b894;
  color: white;
  min-width: 120px;
}

.verify-btn:disabled {
  background: #ccc;
  cursor: not-allowed;
}

.verify-status {
  margin: 12px 0;
  padding: 8px 12px;
  border-radius: 8px;
  text-align: center;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.verify-status.success {
  background: #f0fff4;
  color: #00b894;
  border: 1px solid #68d391;
}

.verify-status.error {
  background: #fff5f5;
  color: #e53e3e;
  border: 1px solid #fc8181;
}

.verify-status.info {
  background: #f0f6ff;
  color: #4A90E2;
  border: 1px solid #90cdf4;
}

.status-icon {
  font-size: 16px;
}

/* AI远程提问预约弹窗 */
.exam-schedule-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease;
}

.exam-schedule-dialog {
  background: white;
  border-radius: 16px;
  padding: 20px;
  width: 90vw;
  max-width: 500px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  position: relative;
  animation: popIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.exam-info {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 16px;
}

.info-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
  font-size: 14px;
}

.info-item:last-child {
  margin-bottom: 0;
}

.info-label {
  color: #666;
  font-weight: 500;
}

.info-value {
  color: #333;
  font-weight: 600;
}

.schedule-form {
  margin-bottom: 20px;
}

.form-label {
  display: block;
  margin-bottom: 8px;
  font-size: 14px;
  font-weight: 500;
  color: #333;
}

.datetime-input {
  width: 100%;
  padding: 12px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  font-size: 14px;
  color: #333;
  font-family: inherit;
}

.datetime-input:focus {
  outline: none;
  border-color: #4A90E2;
  box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}

/* 时间选择器样式 */
.datetime-picker {
  width: 100%;
  padding: 12px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  font-size: 14px;
  color: #333;
  background: white;
  cursor: pointer;
  transition: all 0.3s ease;
}

.datetime-picker:hover {
  border-color: #4A90E2;
}

.datetime-picker-display {
  color: #333;
  font-size: 14px;
  line-height: 1.5;
}

/* 弹窗 */
.reason-dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.kawaii-card {
  background: white;
  border-radius: 16px;
  padding: 20px;
  width: 90vw;
  max-width: 400px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  position: relative;
  animation: popIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

@keyframes popIn {
  from { 
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.dialog-title {
  font-size: 16px;
  margin-top: 0;
  color: #333;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  font-weight: 600;
}

.dialog-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #f0f0f0;
  font-size: 14px;
}

.kawaii-input {
  width: 100%;
  padding: 12px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  font-size: 14px;
  margin-bottom: 16px;
  resize: vertical;
  transition: all 0.3s ease;
  color: #333;
  font-family: inherit;
}

.kawaii-input:focus {
  outline: none;
  border-color: #4A90E2;
  box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}

/* 任务完成申请预览样式 */
.completion-preview {
  margin-top: 8px;
  background: #f9fafb;
  border: 1px solid #eef2f7;
  border-radius: 12px;
  padding: 12px;
}
.preview-content {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.preview-block {
  background: #fff;
  border: 1px solid #eef2f7;
  border-radius: 10px;
  padding: 10px 12px;
}
.preview-label {
  font-size: 13px;
  font-weight: 600;
  color: #34495e;
  margin-bottom: 6px;
}
.preview-proof {
  font-size: 13px;
  color: #2c3e50;
  line-height: 1.6;
  white-space: pre-wrap;
}
.preview-badge {
  border: 1px dashed #e0e6ef;
  border-radius: 10px;
  padding: 10px;
  margin-bottom: 8px;
}
.badge-title {
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 6px;
}
.preview-contributor {
  background: #f8f9fb;
  border: 1px solid #edf2f7;
  border-radius: 8px;
  padding: 8px;
  margin-bottom: 6px;
}
.field-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 12px;
  padding: 2px 0;
}
.field-label { color: #7f8c8d; }
.field-value { color: #2c3e50; }
.field-empty { color: #95a5a6; font-size: 12px; }

.dialog-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.confirm-btn {
  background: #4A90E2;
  color: white;
  min-width: 80px;
  font-size: 13px;
}

.approve-btn {
  background: #00b894;
  color: white;
  min-width: 80px;
  font-size: 13px;
}

.reject-btn {
  background: #e17055;
  color: white;
  min-width: 80px;
  font-size: 13px;
}

.cancel-btn {
  background: #f0f0f0;
  color: #666;
  min-width: 80px;
  font-size: 13px;
}

.postpone-btn {
  background: #ffa726;
  color: white;
  min-width: 80px;
  font-size: 13px;
}

.confirm-btn:hover {
  background: #357abd;
}

.approve-btn:hover {
  background: #00a085;
}

.reject-btn:hover {
  background: #d63031;
}

.cancel-btn:hover {
  background: #e0e0e0;
}

.postpone-btn:hover {
  background: #ff9800;
}

/* 响应式设计 */
@media (max-width: 600px) {
  .message-center {
    padding: 8px 6px 16px 6px;
  }
  
  .type-tabs {
    margin-bottom: 8px;
  }
  
  .type-tab {
    padding: 10px 12px;
    font-size: 13px;
  }
  
  .filters {
    padding: 10px;
    margin-bottom: 8px;
  }
  
  .filter-group {
    min-width: 100px;
  }
  
  .kawaii-select {
    min-width: 80px;
    font-size: 12px;
    padding: 6px 8px;
  }
  
  .kawaii-btn {
    padding: 6px 12px;
    font-size: 12px;
  }
  
  .message-card {
    padding: 12px;
  }
  
  .msg-title {
    font-size: 14px;
  }
  
  .msg-meta {
    font-size: 11px;
  }
  
  .type-badge, .status-badge {
    font-size: 10px;
    padding: 1px 6px;
  }
  
  .msg-time {
    font-size: 10px;
  }
  
  .pagination {
    padding: 10px;
    margin: 12px 0;
  }
  
  .page-info {
    font-size: 12px;
  }
  
  .page-btn {
    min-width: 70px;
    font-size: 11px;
  }
  
  .kawaii-card {
    width: 95vw;
    padding: 16px;
  }
  
  .dialog-title {
    font-size: 15px;
  }
  
  .kawaii-input {
    font-size: 13px;
  }
  
  .confirm-btn, .cancel-btn {
    min-width: 70px;
    font-size: 12px;
  }
}

@media (max-width: 400px) {
  .message-center {
    padding: 6px 4px 12px 4px;
  }
  
  .type-tab {
    padding: 8px 8px;
    font-size: 12px;
  }
  
  .filters {
    padding: 8px;
  }
  
  .filter-group {
    min-width: 80px;
  }
  
  .kawaii-select {
    min-width: 70px;
    font-size: 11px;
    padding: 5px 6px;
  }
  
  .kawaii-btn {
    padding: 5px 10px;
    font-size: 11px;
  }
  
  .message-card {
    padding: 10px;
  }
  
  .msg-title {
    font-size: 13px;
  }
  
  .msg-meta {
    font-size: 10px;
  }
  
  .type-badge, .status-badge {
    font-size: 9px;
    padding: 1px 4px;
  }
  
  .msg-time {
    font-size: 9px;
  }
  
  .pagination {
    padding: 8px;
  }
  
  .page-info {
    font-size: 11px;
  }
  
  .page-btn {
    min-width: 60px;
    font-size: 10px;
  }
  
  .kawaii-card {
    width: 98vw;
    padding: 12px;
  }
  
  .dialog-title {
    font-size: 14px;
  }
  
  .kawaii-input {
    font-size: 12px;
  }
  
  .confirm-btn, .cancel-btn {
    min-width: 60px;
    font-size: 11px;
  }
}

.fade-enter-active, .fade-leave-active {
  transition: all 0.5s cubic-bezier(.4,0,.2,1);
}
.fade-enter-from, .fade-leave-to {
  opacity: 0.5;
  transform: scale(0.98);
  filter: blur(2px);
}

/* 贡献者信息样式 */
.contributor-header {
  display: flex;
  align-items: center;
  margin-bottom: 12px;
  padding: 8px;
  background: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #e9ecef;
}

.contributor-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  margin-right: 12px;
  object-fit: cover;
  border: 2px solid #e9ecef;
}

.contributor-info {
  flex: 1;
}

.contributor-name {
  font-size: 14px;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 2px;
}

.contributor-uid {
  font-size: 12px;
  color: #6c757d;
}

/* 团队信息样式 */
.team-info-preview {
  margin-top: 10px;
}

.team-details {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.team-name {
  font-size: 13px;
  color: #2c3e50;
  font-weight: 500;
}

.team-leader {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  background: #f8f9fb;
  border: 1px solid #edf2f7;
  border-radius: 8px;
  margin-bottom: 8px;
}

.leader-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid #27ae60;
}

.leader-info {
  flex: 1;
}

.leader-name {
  font-size: 13px;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 2px;
}

.leader-uid {
  font-size: 11px;
  color: #7f8c8d;
}

.leader-role-badge {
  background: #e8f5e8;
  color: #27ae60;
  border: 1px solid #d5f4d5;
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 4px;
  font-weight: 500;
}

.team-members {
  margin-top: 4px;
}

.members-label {
  font-size: 12px;
  color: #7f8c8d;
  margin-bottom: 4px;
}

.member-item {
  display: flex;
  align-items: center;
  gap: 8px;
  background: #f8f9fb;
  border: 1px solid #edf2f7;
  border-radius: 6px;
  padding: 6px 8px;
  margin-bottom: 4px;
}

.member-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid #e0e6ef;
}

.member-info {
  flex: 1;
}

.member-name {
  font-size: 12px;
  font-weight: 500;
  color: #2c3e50;
  margin-bottom: 2px;
}

.member-uid {
  font-size: 10px;
  color: #7f8c8d;
}

.member-role {
  font-size: 11px;
  padding: 2px 6px;
  border-radius: 4px;
  font-weight: 500;
}

.leader-role {
  background: #e8f5e8;
  color: #27ae60;
  border: 1px solid #d5f4d5;
}

.member-role:not(.leader-role) {
  background: #e3f2fd;
  color: #1976d2;
  border: 1px solid #bbdefb;
}

/* 候选人选择样式 */
.candidate-selection {
  background: #f8f9fa;
  padding: 15px;
  border-radius: 8px;
  margin: 10px 0;
}

.candidates-list {
  margin-bottom: 15px;
}

.candidate-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: white;
  border: 2px solid #e9ecef;
  border-radius: 8px;
  margin-bottom: 8px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.candidate-item:hover {
  border-color: #007bff;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0, 123, 255, 0.15);
}

.candidate-item.selected {
  border-color: #28a745;
  background: #f8fff8;
  box-shadow: 0 2px 8px rgba(40, 167, 69, 0.15);
}

.candidate-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid #e9ecef;
}

.candidate-info {
  flex: 1;
}

.candidate-name {
  font-size: 14px;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 2px;
}

.candidate-uid {
  font-size: 12px;
  color: #6c757d;
}

.selected-icon {
  color: #28a745;
  font-size: 18px;
  font-weight: bold;
}

.candidate-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}


/* 团队邀请弹窗样式 */
.team-invitation-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease;
}

.team-invitation-dialog {
  background: white;
  border-radius: 16px;
  padding: 20px;
  width: 90vw;
  max-width: 500px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  position: relative;
  animation: popIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.invitation-content {
  margin: 16px 0;
}

/* 邀请人信息区域 */
.inviter-section {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #e9ecef;
  margin-bottom: 12px;
}

.inviter-avatar {
  flex-shrink: 0;
}

.avatar-img {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: 2px solid #007bff;
  object-fit: cover;
}

.inviter-details {
  flex: 1;
}

.inviter-name {
  font-size: 16px;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 4px;
}

.inviter-role {
  font-size: 13px;
  color: #6c757d;
}

/* 团队信息区域 */
.team-section {
  padding: 12px;
  background: #fff;
  border-radius: 8px;
  border: 1px solid #e9ecef;
  margin-bottom: 12px;
}

.team-info {
  display: flex;
  align-items: center;
  gap: 8px;
}

.team-label {
  font-size: 13px;
  color: #6c757d;
  font-weight: 500;
  min-width: 70px;
}

.team-name {
  font-size: 14px;
  color: #495057;
  font-weight: 600;
}

/* 任务信息区域（可点击） */
.task-section {
  padding: 12px;
  background: #fff;
  border-radius: 8px;
  border: 1px solid #007bff;
  cursor: pointer;
  transition: all 0.3s ease;
  margin-bottom: 16px;
}

.task-section:hover {
  background: #f8f9ff;
  border-color: #0056b3;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 123, 255, 0.15);
}

.task-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.task-label {
  font-size: 13px;
  color: #6c757d;
  font-weight: 500;
}

.task-jump-hint {
  font-size: 12px;
  color: #007bff;
  font-weight: 500;
}

.task-title {
  font-size: 15px;
  color: #2c3e50;
  font-weight: 600;
  margin-bottom: 6px;
  line-height: 1.4;
}

.task-content {
  font-size: 13px;
  color: #6c757d;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 任务详细信息样式 */
.task-details {
  margin-top: 12px;
  padding: 12px;
  background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
  border-radius: 12px;
  border: 1px solid #e8ecff;
}

.task-details > div {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  font-size: 13px;
}

.task-details > div:last-child {
  margin-bottom: 0;
}

.detail-label {
  font-weight: 600;
  color: #4a90e2;
  margin-right: 8px;
  min-width: 60px;
}

.detail-value {
  color: #333;
  font-weight: 500;
  flex: 1;
}

/* 任务状态样式 */
.status-pending {
  color: #ff9500;
  background: #fff4e6;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
}

.status-progress {
  color: #4a90e2;
  background: #e8f4ff;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
}

.status-completed {
  color: #52c41a;
  background: #f6ffed;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
}

.status-cancelled {
  color: #ff4d4f;
  background: #fff2f0;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
}

.status-expired {
  color: #8c8c8c;
  background: #f5f5f5;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
}

.status-unknown {
  color: #666;
  background: #f0f0f0;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
}

/* 投票模态框样式 */
.vote-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 20px;
}

.vote-modal {
  position: relative;
  background: white;
  border-radius: 16px;
  padding: 24px;
  max-width: 500px;
  width: 100%;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}

.vote-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 2px solid #f0f0f0;
}

.vote-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #2c3e50;
}

.vote-icon {
  font-size: 20px;
}

.close-btn {
  background: none;
  border: none;
  font-size: 24px;
  color: #999;
  cursor: pointer;
  padding: 4px;
  border-radius: 4px;
  transition: all 0.2s ease;
}

.close-btn:hover {
  background: #f5f5f5;
  color: #666;
}

/* 任务详情部分 */
.task-detail-section {
  background: #f8f9fa;
  border-radius: 12px;
  padding: 16px;
  margin-bottom: 20px;
  border: 1px solid #e9ecef;
}

.task-info {
  margin-bottom: 12px;
}

.task-title {
  font-size: 16px;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 8px;
}

.task-reward {
  color: #28a745;
  font-weight: 600;
  margin-bottom: 4px;
}

.task-status {
  color: #6c757d;
  font-size: 14px;
}

.task-detail-btn {
  background: #007bff;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 8px;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  gap: 6px;
}

.task-detail-btn:hover {
  background: #0056b3;
}

/* 投票进度部分 */
.vote-progress-section {
  margin-bottom: 20px;
}

.progress-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.progress-label {
  font-weight: 600;
  color: #2c3e50;
}

.progress-count {
  font-weight: 600;
  color: #007bff;
  font-size: 16px;
}

.progress-bar {
  width: 100%;
  height: 8px;
  background: #e9ecef;
  border-radius: 4px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #28a745, #20c997);
  transition: width 0.3s ease;
}

.progress-required {
  font-size: 12px;
  color: #6c757d;
  margin-left: 8px;
}

.vote-rules {
  margin-top: 8px;
  padding: 8px 12px;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 3px solid #007bff;
}

.rule-text {
  font-size: 12px;
  color: #495057;
  line-height: 1.4;
}

/* 投票结果显示 */
.vote-results {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 12px 0;
}

/* 任务修改申请弹窗样式 */
.task-modification-modal {
  max-width: 90vw;
  max-height: 80vh;
  overflow-y: auto;
}

.modification-info {
  margin-bottom: 20px;
}

.info-label {
  font-weight: 600;
  color: #333;
  margin-bottom: 10px;
  display: block;
}

.modification-details {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 15px;
}

.detail-item {
  display: flex;
  margin-bottom: 10px;
  align-items: flex-start;
}

.detail-item:last-child {
  margin-bottom: 0;
}

.detail-label {
  font-weight: 600;
  color: #666;
  min-width: 80px;
  margin-right: 10px;
}

.detail-value {
  color: #333;
  flex: 1;
  word-break: break-word;
}

.badge-preview {
  margin-top: 20px;
}

.badge-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
}

.badge-item {
  background: #e3f2fd;
  border: 1px solid #bbdefb;
  border-radius: 8px;
  padding: 12px;
}

.badge-name {
  font-weight: 600;
  color: #1976d2;
  display: block;
  margin-bottom: 4px;
}

.badge-level {
  color: #666;
  font-size: 14px;
  margin-bottom: 4px;
  display: block;
}

.badge-description {
  color: #555;
  font-size: 14px;
  line-height: 1.4;
}

.result-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 3px solid #dee2e6;
}

.result-label {
  font-weight: 600;
  color: #495057;
}

.result-count {
  font-weight: 600;
  font-size: 16px;
}

.result-count.approve {
  color: #28a745;
}

.result-count.reject {
  color: #dc3545;
}

.result-count.pending {
  color: #6c757d;
}

/* 投票原因部分 */
.vote-reason-section {
  margin-bottom: 24px;
}

.reason-label {
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 8px;
}

.reason-content {
  background: #f8f9fa;
  padding: 12px;
  border-radius: 8px;
  border: 1px solid #e9ecef;
  color: #495057;
  line-height: 1.5;
}

/* 投票按钮部分 */
.vote-buttons {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
}

.vote-buttons .kawaii-btn {
  flex: 1;
  padding: 12px 16px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
}

.vote-buttons .approve-btn {
  background: linear-gradient(135deg, #28a745, #20c997);
  color: white;
  box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
}

.vote-buttons .approve-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(40, 167, 69, 0.4);
}

.vote-buttons .reject-btn {
  background: linear-gradient(135deg, #dc3545, #e74c3c);
  color: white;
  box-shadow: 0 4px 12px rgba(220, 53, 69, 0.3);
}

.vote-buttons .reject-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(220, 53, 69, 0.4);
}

.vote-buttons .abstain-btn {
  background: linear-gradient(135deg, #6c757d, #868e96);
  color: white;
  box-shadow: 0 4px 12px rgba(108, 117, 125, 0.3);
}

.vote-buttons .abstain-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(108, 117, 125, 0.4);
}

.vote-buttons .kawaii-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

/* 加载状态 */
.vote-loading {
  text-align: center;
  color: #6c757d;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.loading-icon {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

/* 响应式设计 */
@media (max-width: 480px) {
  .vote-modal {
    margin: 10px;
    padding: 16px;
  }
  
  .vote-buttons {
    flex-direction: column;
  }
  
  .vote-buttons .kawaii-btn {
    flex: none;
  }
}
</style>